<commit_before>"""
Plot class.

$Id$
"""
__version__='$Revision$'

import palette
import copy

from bitmap import HSVBitmap, RGBBitmap, PaletteBitmap, Bitmap

from numpy.oldnumeric import zeros, ones, Float, divide,ravel,clip,array

from topo.base.parameterizedobject import ParameterizedObject
from topo.base.parameterclasses import Number
from topo.base.sheetview import SheetView
from topo.base.sheet import Slice
from topo.base.sheetcoords import SheetCoordinateSystem

### JCALERT!
### - Re-write the test file, taking the new changes into account.
### - I have to change the order: situate, plot_bb and (normalize)
### - There should be a way to associate the density explicitly
###   with the sheet_view_dict, because it must match all SheetViews
###   in that dictionary.  Maybe as a tuple?
### - Fix the plot name handling along with the view_info sheetview attribute
### - Get rid of release_sheetviews.

class Plot(ParameterizedObject):
     """
     Simple Plot object constructed from a specified PIL image.
     """

     staleness_warning=Number(default=10,bounds=(0,None),doc="""
       Time length allowed between bitmaps making up a single plot before warning.

       If the difference between the SheetView with the earliest
       timestamp and the one with the latest timestamp is larger
       than this parameter's value, produce a warning.
       """)

     def __init__(self,image=None,**params):

          super(Plot,self).__init__(**params)
          self.bitmap = Bitmap(image)
          self._orig_bitmap = self.bitmap # Copy kept in case of later rescaling
          self.scale_factor=1.0
          self.plot_src_name = ''
          self.precedence = 0.0
          # If False, this plot should be left in its native size
          # pixel-for-pixel, (e.g. for a color key or similar static
          # image), rather than being resized as necessary.
          self.resize=False

          # Time at which the bitmaps were created
          self.timestamp = -1

     def rescale(self,scale_factor):
          """
          Change the size of this image by the specified numerical factor.

          The original image is kept as-is in _orig_bitmap; the scaled
          image is stored in bitmap.  The scale_factor argument is
          take as relative to the current scaling of the bitmap.  For
          instance, calling scale(1.5) followed by scale(2.0) will
          yield a final scale of 3.0, not 2.0.
          """
          self.scale_factor *= scale_factor

          if (self.bitmap):
              self.bitmap = copy.copy(self._orig_bitmap)
              self.bitmap.image = self._orig_bitmap.zoom(self.scale_factor)

def make_template_plot(channels,sheet_view_dict,density=None,
              plot_bounding_box=None,normalize=False,name='None'):
     """
     Factory function for constructing a Plot object whose type is not yet known.

     Typically, a Plot will be constructed through this call, because
     it selects the appropriate type automatically, rather than calling
     one of the Plot subclasses automatically.  See Plot.__init__ for
     a description of the arguments.
     """
     plot_types=[SHCPlot,RGBPlot,PalettePlot]
     for pt in plot_types:
         plot = pt(channels,sheet_view_dict,density,plot_bounding_box,normalize,name=name)
         if plot.bitmap != None:
	     return plot

     ParameterizedObject(name="make_template_plot").verbose('No',name,'plot constructed for this Sheet')
     return None

class TemplatePlot(Plot):
    """
    A bitmap-based plot as specified by a plot template (or plot channels).
    """

    def __init__(self,channels,sheet_view_dict,density,
                 plot_bounding_box,normalize,**params):
	"""
        Build a plot out of a set of SheetViews as determined by a plot_template.

        channels is a plot_template, i.e. a dictionary with keys
        (i.e. 'Strength','Hue','Confidence' ...).  Each key typically
        has a string value naming specifies a SheetView in
        sheet_view_dict, though specific channels may contain other
        types of information as required by specific Plot subclasses.
        channels that are not used by a particular Plot subclass will
        silently be ignored.

	sheet_view_dict is a dictionary of SheetViews, generally (but
        not necessarily) belonging to a Sheet object.

	density is the density of the Sheet whose sheet_view_dict was
	passed.

	plot_bounding_box is the outer bounding_box of the plot to
        apply if specified.  If not, the bounds of
        the smallest SheetView are used.

	normalize specifies whether the Plot should be normalized to
	fill the maximum dynamic range, i.e. 0.0 to 1.0.  If not
        normalized, values are clipped at 1.0.

        name (which is inherited from ParameterizedObject) specifies the name
        to use for this plot.
        """

        super(TemplatePlot,self).__init__(**params)
        # for a template plot, resize is True by default
        self.resize=True
        self.bitmap = None

	self.channels = channels
	self.view_dict = sheet_view_dict
	# bounds of the situated plotting area
	self.plot_bounding_box = plot_bounding_box

        ### JCALERT ! The problem of displaying the right plot name is still reviewed
        ### at the moment we have the plot_src_name and name attribute that are used for the label.
        ### generally the name is set to the plot_template name, except for connection
        # set the name of the sheet that provides the SheetViews
        # combined with the self.name parameter when creating the plot (which is generally
        # the name of the plot_template), it provides the necessary information for displaying plot label
	self._set_plot_src_name()

	# # Eventually: support other type of plots (e.g vector fields...) using
        # # something like:
	# def annotated_bitmap(self):
        # enable other construction....

        self.warn_time=-2

    def _get_matrix(self,key):
        """
	Private Plot routine that just retrieves the matrix view (if any)
        associated with a given key in the Plot.channels.

	If the key is in channels, the corresponding sheetviews is looked
        for in the view_dict, and its associated matrix is returned if found.
        None is returned otherwise.

	(It just deals with the fact that channels can be None, or that the keys
	specified by channels can potentially refer to no SheetViews in the dict).
        """
        sheet_view_key = self.channels.get(key,None)
	sv = self.view_dict.get(sheet_view_key, None)
        if sv == None:
	    matrix = None
	else:
	    view = sv.view()
	    matrix = view[0]

            # Calculate timestamp for this plot
            timestamp = sv.timestamp
            if timestamp >=0:
                if self.timestamp < 0:
                    self.timestamp = timestamp
                elif abs(timestamp - self.timestamp) > self.staleness_warning:
                    # Warns only once for each old matrix used
                    if self.warn_time != min(timestamp, self.timestamp):
                        self.warning("Combining SheetViews from different times (%s,%s); see staleness_warning" %
                                     (timestamp, self.timestamp))
                        self.warn_time = min(timestamp, self.timestamp)

        return matrix

    def _set_plot_src_name(self):
	""" Set the Plot plot_src_name. Called when Plot is created"""
	for key in self.channels:
	    sheet_view_key = self.channels.get(key,None)
	    sv = self.view_dict.get(sheet_view_key, None)
	    if sv != None:
                 self.plot_src_name = sv.src_name
                 self.precedence = sv.precedence

    ### JCALERT: This could be inserted in the code of get_matrix
    def _get_shape_and_box(self):
	"""
	Sub-function used by plot: get the matrix shape and the bounding box
        of the SheetViews that constitue the TemplatePlot.
    	"""
	for name in self.channels.values():
                sv = self.view_dict.get(name,None)
                if sv != None:
                     shape = sv.view()[0].shape
                     box = sv.view()[1]

        return shape,box

    def _normalize(self,a):
        """
        Normalize an array s.
        In case of a constant array, ones is returned for value greater than zero,
        and zeros in case of value inferior or equal to zero.
        """
        a_offset = a-min(a.ravel())
        max_a_offset = max(a_offset.ravel())
        if max_a_offset>0:
             a = divide(a_offset,float(max_a_offset))
        else:
             if min(a.ravel())<=0:
                  a=zeros(a.shape,Float)
             else:
                  a=ones(a.shape,Float)
        return a

    ### JC: maybe density can become an attribute of the TemplatePlot?
    def _re_bound(self,plot_bounding_box,mat,box,density):

        # CEBHACKALERT: for Julien...
        # If plot_bounding_box is that of a Sheet, it will already have been
        # setup so that the density in the x direction and the density in the
        # y direction are equal.
        # If plot_bounding_box comes from elsewhere (i.e. you create it from
        # arbitrary bounds), it might need to be adjusted to ensure the density
        # in both directions is the same (see Sheet.__init__()). I don't know where
        # you want to do that; presumably the code should be common to Sheet and
        # where it's used in the plotting?
        #
        # It's possible we can move some of the functionality
        # into SheetCoordinateSystem.
        if plot_bounding_box.containsbb_exclusive(box):

             ct = SheetCoordinateSystem(plot_bounding_box,density,density)
             new_mat = zeros(ct.shape,Float)

             ct2 = SheetCoordinateSystem(plot_bounding_box,density,density)
             r1,r2,c1,c2 = Slice(box,ct2)
             new_mat[r1:r2,c1:c2] = mat
        else:
             s=Slice(plot_bounding_box,SheetCoordinateSystem(box,density,density))
             s.crop_to_sheet()
             new_mat = s.submatrix(mat)

        return new_mat

class SHCPlot(TemplatePlot):
    """
    Bitmap plot based on Strength, Hue, and Confidence matrices.

    Constructs an HSV (hue, saturation, and value) plot by choosing
    the appropriate matrix for each channel.
    """

    def __init__(self,channels,sheet_view_dict,density,
                 plot_bounding_box,normalize,**params):
	super(SHCPlot,self).__init__(channels,sheet_view_dict,density,
				   plot_bounding_box,normalize,**params)

	# catching the empty plot exception
	s_mat = self._get_matrix('Strength')
	h_mat = self._get_matrix('Hue')
	c_mat = self._get_matrix('Confidence')

        # If it is an empty plot: self.bitmap=None
        if (s_mat==None and c_mat==None and h_mat==None):
	    self.debug('Empty plot.')

        # Otherwise, we construct self.bitmap according to what is specified by the channels.
	else:

            shape,box = self._get_shape_and_box()

            hue,sat,val = self.__make_hsv_matrices((s_mat,h_mat,c_mat),shape,normalize)

            if self.plot_bounding_box == None:
               self.plot_bounding_box = box

            hue = self._re_bound(self.plot_bounding_box,hue,box,density)
            sat = self._re_bound(self.plot_bounding_box,sat,box,density)
            val = self._re_bound(self.plot_bounding_box,val,box,density)

            self.bitmap = HSVBitmap(hue,sat,val)

        self._orig_bitmap=self.bitmap

    def __make_hsv_matrices(self,hsc_matrices,shape,normalize):
	"""
	Sub-function of plot() that return the h,s,v matrices corresponding
	to the current matrices in sliced_matrices_dict. The shape of the matrices
        in the dict is passed, as well as the normalize boolean parameter.
	The result specified a bitmap in hsv coordinate.

        Applies normalizing and cropping if required.
	"""
	zero=zeros(shape,Float)
	one=ones(shape,Float)

	s,h,c = hsc_matrices
	# Determine appropriate defaults for each matrix
	if s is None: s=one # Treat as full strength by default
	if c is None: c=one # Treat as full confidence by default
	if h is None:       # No color, gray-scale plot.
	    h=zero
	    c=zero

        # If normalizing, offset the matrix so that the minimum
        # value is 0.0 and then scale to make the maximum 1.0
        if normalize:
             s=self._normalize(s)

        # This translation from SHC to HSV is valid only for black backgrounds;
        # it will need to be extended also to support white backgrounds.
	hue,sat,val=h,c,s
	return (hue,sat,val)

class RGBPlot(TemplatePlot):
  """
  Bitmap plot based on Red, Green, and Blue matrices.

  Construct an RGB (red, green, and blue) plot from the Red, Green,
  and Blue channels.
  """
  def __init__(self,channels,sheet_view_dict,density,
                 plot_bounding_box,normalize,**params):

       super(RGBPlot,self).__init__(channels,sheet_view_dict,density,
				   plot_bounding_box,normalize,**params)

       # catching the empty plot exception
       r_mat = self._get_matrix('Red')
       g_mat = self._get_matrix('Green')
       b_mat = self._get_matrix('Blue')

       # If it is an empty plot: self.bitmap=None
       if (r_mat==None and g_mat==None and b_mat==None):
            self.debug('Empty plot.')

            # Otherwise, we construct self.bitmap according to what is specified by the channels.
       else:

            shape,box = self._get_shape_and_box()

            red,green,blue = self.__make_rgb_matrices((r_mat,g_mat,b_mat),shape,normalize)

            if self.plot_bounding_box == None:
               self.plot_bounding_box = box

            red = self._re_bound(self.plot_bounding_box,red,box,density)
            green = self._re_bound(self.plot_bounding_box,green,box,density)
            blue = self._re_bound(self.plot_bounding_box,blue,box,density)

            self.bitmap = RGBBitmap(red,green,blue)

       self._orig_bitmap=self.bitmap

  def __make_rgb_matrices(self, rgb_matrices,shape,normalize):
	"""
	Sub-function of plot() that return the h,s,v matrices
	corresponding to the current matrices in
	sliced_matrices_dict. The shape of the matrices in the dict is
	passed, as well as the normalize boolean parameter.  The
	result specified a bitmap in hsv coordinate.

        Applies normalizing and cropping if required.
	"""
	zero=zeros(shape,Float)
	one=ones(shape,Float)

	r,g,b = rgb_matrices
	# Determine appropriate defaults for each matrix
	if r is None: r=zero
	if g is None: g=zero
	if b is None: b=zero

        if normalize:
             r = self._normalize(r)
             g = self._normalize(g)
             b = self._normalize(b)

	return (r,g,b)

class PalettePlot(TemplatePlot):
     """
     Bitmap plot based on a Strength matrix, with optional colorization.

     Not yet implemented.

     When implemented, construct an RGB plot from a Strength channel,
     optionally colorized using a specified Palette.
     """

     def __init__(self,channels,sheet_view_dict,density,
                  plot_bounding_box,normalize,**params):

          super(PalettePlot,self).__init__(channels,sheet_view_dict,density,
                                           plot_bounding_box,normalize,**params)

          ### JABHACKALERT: To implement the class: If Strength is present,
          ### ask for Palette if it's there, and make a PaletteBitmap.

<commit_msg>Added set_scale option, to complement rescale<commit_after>"""
Plot class.

$Id$
"""
__version__='$Revision$'

import palette
import copy

from bitmap import HSVBitmap, RGBBitmap, PaletteBitmap, Bitmap

from numpy.oldnumeric import zeros, ones, Float, divide,ravel,clip,array

from topo.base.parameterizedobject import ParameterizedObject
from topo.base.parameterclasses import Number
from topo.base.sheetview import SheetView
from topo.base.sheet import Slice
from topo.base.sheetcoords import SheetCoordinateSystem

### JCALERT!
### - Re-write the test file, taking the new changes into account.
### - I have to change the order: situate, plot_bb and (normalize)
### - There should be a way to associate the density explicitly
###   with the sheet_view_dict, because it must match all SheetViews
###   in that dictionary.  Maybe as a tuple?
### - Fix the plot name handling along with the view_info sheetview attribute
### - Get rid of release_sheetviews.

class Plot(ParameterizedObject):
     """
     Simple Plot object constructed from a specified PIL image.
     """

     staleness_warning=Number(default=10,bounds=(0,None),doc="""
       Time length allowed between bitmaps making up a single plot before warning.

       If the difference between the SheetView with the earliest
       timestamp and the one with the latest timestamp is larger
       than this parameter's value, produce a warning.
       """)

     def __init__(self,image=None,**params):

          super(Plot,self).__init__(**params)
          self._orig_bitmap = Bitmap(image)
          self.bitmap = self._orig_bitmap # Possibly scaled copy (at first identical)
          self.scale_factor=1.0
          self.plot_src_name = ''
          self.precedence = 0.0
          # If False, this plot should be left in its native size
          # pixel-for-pixel, (e.g. for a color key or similar static
          # image), rather than being resized as necessary.
          self.resize=False

          # Time at which the bitmaps were created
          self.timestamp = -1

     def rescale(self,scale_factor):
          """
          Change the size of this image by the specified numerical factor.

          The original image is kept as-is in _orig_bitmap; the scaled
          image is stored in bitmap.  The scale_factor argument is
          taken as relative to the current scaling of the bitmap.  For
          instance, calling scale(1.5) followed by scale(2.0) will
          yield a final scale of 3.0, not 2.0.
          """
          self.scale_factor *= scale_factor

          if (self._orig_bitmap):
              self.bitmap = copy.copy(self._orig_bitmap)
              self.bitmap.image = self._orig_bitmap.zoom(self.scale_factor)

     def set_scale(self,scale_factor):
          """
          Specify the numerical value of the scaling factor for this image.

          The original image is kept as-is in _orig_bitmap; the scaled
          image is stored in bitmap.  The scale_factor argument is
          taken as relative to the original size of the bitmap.  For
          instance, calling scale(1.5) followed by scale(2.0) will
          yield a final scale of 2.0, not 3.0.
          """
          self.scale_factor = scale_factor

          if (self._orig_bitmap):
              self.bitmap = copy.copy(self._orig_bitmap)
              self.bitmap.image = self._orig_bitmap.zoom(self.scale_factor)

def make_template_plot(channels,sheet_view_dict,density=None,
              plot_bounding_box=None,normalize=False,name='None'):
     """
     Factory function for constructing a Plot object whose type is not yet known.

     Typically, a Plot will be constructed through this call, because
     it selects the appropriate type automatically, rather than calling
     one of the Plot subclasses automatically.  See Plot.__init__ for
     a description of the arguments.
     """
     plot_types=[SHCPlot,RGBPlot,PalettePlot]
     for pt in plot_types:
         plot = pt(channels,sheet_view_dict,density,plot_bounding_box,normalize,name=name)
         if plot.bitmap != None:
	     return plot

     ParameterizedObject(name="make_template_plot").verbose('No',name,'plot constructed for this Sheet')
     return None

class TemplatePlot(Plot):
    """
    A bitmap-based plot as specified by a plot template (or plot channels).
    """

    def __init__(self,channels,sheet_view_dict,density,
                 plot_bounding_box,normalize,**params):
	"""
        Build a plot out of a set of SheetViews as determined by a plot_template.

        channels is a plot_template, i.e. a dictionary with keys
        (i.e. 'Strength','Hue','Confidence' ...).  Each key typically
        has a string value naming specifies a SheetView in
        sheet_view_dict, though specific channels may contain other
        types of information as required by specific Plot subclasses.
        channels that are not used by a particular Plot subclass will
        silently be ignored.

	sheet_view_dict is a dictionary of SheetViews, generally (but
        not necessarily) belonging to a Sheet object.

	density is the density of the Sheet whose sheet_view_dict was
	passed.

	plot_bounding_box is the outer bounding_box of the plot to
        apply if specified.  If not, the bounds of
        the smallest SheetView are used.

	normalize specifies whether the Plot should be normalized to
	fill the maximum dynamic range, i.e. 0.0 to 1.0.  If not
        normalized, values are clipped at 1.0.

        name (which is inherited from ParameterizedObject) specifies the name
        to use for this plot.
        """

        super(TemplatePlot,self).__init__(**params)
        # for a template plot, resize is True by default
        self.resize=True
        self.bitmap = None

	self.channels = channels
	self.view_dict = sheet_view_dict
	# bounds of the situated plotting area
	self.plot_bounding_box = plot_bounding_box

        ### JCALERT ! The problem of displaying the right plot name is still reviewed
        ### at the moment we have the plot_src_name and name attribute that are used for the label.
        ### generally the name is set to the plot_template name, except for connection
        # set the name of the sheet that provides the SheetViews
        # combined with the self.name parameter when creating the plot (which is generally
        # the name of the plot_template), it provides the necessary information for displaying plot label
	self._set_plot_src_name()

	# # Eventually: support other type of plots (e.g vector fields...) using
        # # something like:
	# def annotated_bitmap(self):
        # enable other construction....

        self.warn_time=-2

    def _get_matrix(self,key):
        """
	Private Plot routine that just retrieves the matrix view (if any)
        associated with a given key in the Plot.channels.

	If the key is in channels, the corresponding sheetviews is looked
        for in the view_dict, and its associated matrix is returned if found.
        None is returned otherwise.

	(It just deals with the fact that channels can be None, or that the keys
	specified by channels can potentially refer to no SheetViews in the dict).
        """
        sheet_view_key = self.channels.get(key,None)
	sv = self.view_dict.get(sheet_view_key, None)
        if sv == None:
	    matrix = None
	else:
	    view = sv.view()
	    matrix = view[0]

            # Calculate timestamp for this plot
            timestamp = sv.timestamp
            if timestamp >=0:
                if self.timestamp < 0:
                    self.timestamp = timestamp
                elif abs(timestamp - self.timestamp) > self.staleness_warning:
                    # Warns only once for each old matrix used
                    if self.warn_time != min(timestamp, self.timestamp):
                        self.warning("Combining SheetViews from different times (%s,%s); see staleness_warning" %
                                     (timestamp, self.timestamp))
                        self.warn_time = min(timestamp, self.timestamp)

        return matrix

    def _set_plot_src_name(self):
	""" Set the Plot plot_src_name. Called when Plot is created"""
	for key in self.channels:
	    sheet_view_key = self.channels.get(key,None)
	    sv = self.view_dict.get(sheet_view_key, None)
	    if sv != None:
                 self.plot_src_name = sv.src_name
                 self.precedence = sv.precedence

    ### JCALERT: This could be inserted in the code of get_matrix
    def _get_shape_and_box(self):
	"""
	Sub-function used by plot: get the matrix shape and the bounding box
        of the SheetViews that constitue the TemplatePlot.
    	"""
	for name in self.channels.values():
                sv = self.view_dict.get(name,None)
                if sv != None:
                     shape = sv.view()[0].shape
                     box = sv.view()[1]

        return shape,box

    def _normalize(self,a):
        """
        Normalize an array s.
        In case of a constant array, ones is returned for value greater than zero,
        and zeros in case of value inferior or equal to zero.
        """
        a_offset = a-min(a.ravel())
        max_a_offset = max(a_offset.ravel())
        if max_a_offset>0:
             a = divide(a_offset,float(max_a_offset))
        else:
             if min(a.ravel())<=0:
                  a=zeros(a.shape,Float)
             else:
                  a=ones(a.shape,Float)
        return a

    ### JC: maybe density can become an attribute of the TemplatePlot?
    def _re_bound(self,plot_bounding_box,mat,box,density):

        # CEBHACKALERT: for Julien...
        # If plot_bounding_box is that of a Sheet, it will already have been
        # setup so that the density in the x direction and the density in the
        # y direction are equal.
        # If plot_bounding_box comes from elsewhere (i.e. you create it from
        # arbitrary bounds), it might need to be adjusted to ensure the density
        # in both directions is the same (see Sheet.__init__()). I don't know where
        # you want to do that; presumably the code should be common to Sheet and
        # where it's used in the plotting?
        #
        # It's possible we can move some of the functionality
        # into SheetCoordinateSystem.
        if plot_bounding_box.containsbb_exclusive(box):

             ct = SheetCoordinateSystem(plot_bounding_box,density,density)
             new_mat = zeros(ct.shape,Float)

             ct2 = SheetCoordinateSystem(plot_bounding_box,density,density)
             r1,r2,c1,c2 = Slice(box,ct2)
             new_mat[r1:r2,c1:c2] = mat
        else:
             s=Slice(plot_bounding_box,SheetCoordinateSystem(box,density,density))
             s.crop_to_sheet()
             new_mat = s.submatrix(mat)

        return new_mat

class SHCPlot(TemplatePlot):
    """
    Bitmap plot based on Strength, Hue, and Confidence matrices.

    Constructs an HSV (hue, saturation, and value) plot by choosing
    the appropriate matrix for each channel.
    """

    def __init__(self,channels,sheet_view_dict,density,
                 plot_bounding_box,normalize,**params):
	super(SHCPlot,self).__init__(channels,sheet_view_dict,density,
				   plot_bounding_box,normalize,**params)

	# catching the empty plot exception
	s_mat = self._get_matrix('Strength')
	h_mat = self._get_matrix('Hue')
	c_mat = self._get_matrix('Confidence')

        # If it is an empty plot: self.bitmap=None
        if (s_mat==None and c_mat==None and h_mat==None):
	    self.debug('Empty plot.')

        # Otherwise, we construct self.bitmap according to what is specified by the channels.
	else:

            shape,box = self._get_shape_and_box()

            hue,sat,val = self.__make_hsv_matrices((s_mat,h_mat,c_mat),shape,normalize)

            if self.plot_bounding_box == None:
               self.plot_bounding_box = box

            hue = self._re_bound(self.plot_bounding_box,hue,box,density)
            sat = self._re_bound(self.plot_bounding_box,sat,box,density)
            val = self._re_bound(self.plot_bounding_box,val,box,density)

            self.bitmap = HSVBitmap(hue,sat,val)

        self._orig_bitmap=self.bitmap

    def __make_hsv_matrices(self,hsc_matrices,shape,normalize):
	"""
	Sub-function of plot() that return the h,s,v matrices corresponding
	to the current matrices in sliced_matrices_dict. The shape of the matrices
        in the dict is passed, as well as the normalize boolean parameter.
	The result specified a bitmap in hsv coordinate.

        Applies normalizing and cropping if required.
	"""
	zero=zeros(shape,Float)
	one=ones(shape,Float)

	s,h,c = hsc_matrices
	# Determine appropriate defaults for each matrix
	if s is None: s=one # Treat as full strength by default
	if c is None: c=one # Treat as full confidence by default
	if h is None:       # No color, gray-scale plot.
	    h=zero
	    c=zero

        # If normalizing, offset the matrix so that the minimum
        # value is 0.0 and then scale to make the maximum 1.0
        if normalize:
             s=self._normalize(s)

        # This translation from SHC to HSV is valid only for black backgrounds;
        # it will need to be extended also to support white backgrounds.
	hue,sat,val=h,c,s
	return (hue,sat,val)

class RGBPlot(TemplatePlot):
  """
  Bitmap plot based on Red, Green, and Blue matrices.

  Construct an RGB (red, green, and blue) plot from the Red, Green,
  and Blue channels.
  """
  def __init__(self,channels,sheet_view_dict,density,
                 plot_bounding_box,normalize,**params):

       super(RGBPlot,self).__init__(channels,sheet_view_dict,density,
				   plot_bounding_box,normalize,**params)

       # catching the empty plot exception
       r_mat = self._get_matrix('Red')
       g_mat = self._get_matrix('Green')
       b_mat = self._get_matrix('Blue')

       # If it is an empty plot: self.bitmap=None
       if (r_mat==None and g_mat==None and b_mat==None):
            self.debug('Empty plot.')

            # Otherwise, we construct self.bitmap according to what is specified by the channels.
       else:

            shape,box = self._get_shape_and_box()

            red,green,blue = self.__make_rgb_matrices((r_mat,g_mat,b_mat),shape,normalize)

            if self.plot_bounding_box == None:
               self.plot_bounding_box = box

            red = self._re_bound(self.plot_bounding_box,red,box,density)
            green = self._re_bound(self.plot_bounding_box,green,box,density)
            blue = self._re_bound(self.plot_bounding_box,blue,box,density)

            self.bitmap = RGBBitmap(red,green,blue)

       self._orig_bitmap=self.bitmap

  def __make_rgb_matrices(self, rgb_matrices,shape,normalize):
	"""
	Sub-function of plot() that return the h,s,v matrices
	corresponding to the current matrices in
	sliced_matrices_dict. The shape of the matrices in the dict is
	passed, as well as the normalize boolean parameter.  The
	result specified a bitmap in hsv coordinate.

        Applies normalizing and cropping if required.
	"""
	zero=zeros(shape,Float)
	one=ones(shape,Float)

	r,g,b = rgb_matrices
	# Determine appropriate defaults for each matrix
	if r is None: r=zero
	if g is None: g=zero
	if b is None: b=zero

        if normalize:
             r = self._normalize(r)
             g = self._normalize(g)
             b = self._normalize(b)

	return (r,g,b)

class PalettePlot(TemplatePlot):
     """
     Bitmap plot based on a Strength matrix, with optional colorization.

     Not yet implemented.

     When implemented, construct an RGB plot from a Strength channel,
     optionally colorized using a specified Palette.
     """

     def __init__(self,channels,sheet_view_dict,density,
                  plot_bounding_box,normalize,**params):

          super(PalettePlot,self).__init__(channels,sheet_view_dict,density,
                                           plot_bounding_box,normalize,**params)

          ### JABHACKALERT: To implement the class: If Strength is present,
          ### ask for Palette if it's there, and make a PaletteBitmap.

<|endoftext|>


<commit_before>"""
Base Plot environment that manipulates SheetViews and has Histogram information

A plot contains information about one or more SheetViews that it combines
to create a single matrix that can be displayed as a bitmap.  Associated with
this single (constructed) box of information are multiple histograms that
are stored within the base plot object.

The plot objects are clustered together within a PlotGroup Class which
arranges multiple plots into a single figure on the screen.

The PlotEngine class uses PlotTemplate objects, embedded within
PlotGroupTemplate objects to create the Plots that will generate the
bitmaps that will be displayed on screen or saved to disk.  A
PlotTemplate is essentially a dictionary of keyed information,
containing sheet names, or channel settings.

Kinds of PlotTemplates:
    SHC Plots:
        Keys:
            Strength   - SheetView dictionary key
            Hue        - SheetView dictionary key
            Confidence - SheetView dictionary key
    Unit Weights Plots:
        Keys:
            Location   - (x,y) tuple
            Sheet_name - Name of sheet to pull unit weights
    Projection Plots:
        Keys:
            Density    - Density to plot projection weights
            Projection_name - Name of projection to plot.  Looks at
                .src sheet_view_dict

$Id$
"""
__version__='$Revision$'
### JABHACKALERT!
###
### The code in this file has not yet been reviewed, and may need
### substantial changes.

import sys
import types

from Numeric import zeros, ones, Float, divide

from topo.base.topoobject import TopoObject
from topo.base.utils import flatten
from topo.base.sheetview import *
from bitmap import matrix_hsv_to_rgb, WHITE_BACKGROUND, BLACK_BACKGROUND
from histogram import Histogram
from topo.base.parameter import Dynamic
from Numeric import array
import palette as palette
import MLab

# Types of plots that Plot knows how to create from input matrices.
RGB = 'RGB'
HSV = 'HSV'
SHC = 'HSV'  # For now treat as an HSV, but reorder the channels.
COLORMAP = 'COLORMAP'

class PlotTemplate(TopoObject):
    """
    Container class for a plot object definition.  This is separate
    from a Plot object since it defines how to create a Plot object
    and should be contained within a PlotGroupTemplate.  The
    PlotEngine will create the requested plot type given the template
    definition.  The templates are used so that standard plot types
    can be redefined at the users convenience.

    For example, 'Activity' maps are defined as:
    activity_template = PlotTemplate({'Strength'   : 'Activity',
                                      'Hue'        : None,
                                      'Confidence' : None})
    """

    def __init__(self, channels=None,**params):
        super(PlotTemplate,self).__init__(**params)
        #self.background = Dynamic(default=background)
        self.channels = channels

### JABHACKALERT!  This class should accept SheetViews, with no
### reference to anything of type Sheet, and no plotting of anything
### else.  It is ridiculously too difficult to understand and maintain
### in its current form.  Any operation that depends on Sheet, such as
### looking up SheetViews by name, or releasing SheetViews, should be
### moved out of it.  PlotTemplate might be able to do such lookup,
### because it's called for every Sheet, but if so it should always
### map consisently from a name string to a SheetView, without trying
### to handle all these special cases.  These classes are trying to be
### way too smart; they should simply plot whatever they are given.
class Plot(TopoObject):
    """
    Class that can construct a single bitmap plot from one or more
    SheetViews, optionally including a Histogram.  The bitmap is just
    stored for future use, e.g. as part of a PlotGroup of related
    plots displayed within one GUI window.
    """
    background = Dynamic(default=BLACK_BACKGROUND)
    palette_ = Dynamic(default=palette.Monochrome)

    def __init__(self, (channel_1, channel_2, channel_3), plot_type, sheet=None,
                 normalize=False, **params):
        """
        channel_1, channel_2, and channel_3 all have one of two types:
        It is either a key that points to a stored SheetView on the
        variable sheet, or
        It is a SheetView object.

        plot_type has three options: HSV, COLORMAP, or RGB.  HSV plots
        will be converted to an RGB matrix triple.  COLORMAPs take in
        a single channel, since each pixel should be a single value,
        that should be selected from a palette.  RGB plots are
        three-channel plots that are not mixed together like HSV,
        since it is assumed the GUI display will be in RGB.

        sheet gives the object that the three channels will request
        SheetViews from if a String has been passed in.  Valid options
        are: None, Single Sheet, or None.
        TODO: TRIPLE OF SHEETS, ONE FOR EACH CHANNEL, IS CURRENTLY NOT
        IMPLEMENTED.  CURRENT IMPLEMENTATION REQUIRES THAT ALL
        CHANNELS DERIVE FROM THE SAME SHEET.  SHOULD EXTEND
        self.source SO THAT DIFFERENT SHEET INPUTS ARE POSSIBLE
        (7/2005)

        If each of the channels are preconstructed SheetViews, there
        is no reason to pass in the sheet, and therefore it can be
        left blank.

        Named parameter 'name' is being inherited from TopoObject

        WHAT ABOUT BOUNDINGREGIONS?
        """
        super(Plot,self).__init__(**params)

        self.source = sheet

        self.channels = (channel_1, channel_2, channel_3)
        self.plot_type = plot_type
        self.view_info = {}
        self.cropped = False
        self.histograms = []
        self.channel_views = []
        self.matrices = []         # Will hold 3 2D matrices.
        self.normalize = normalize

    def shape(self):
        """ Return the shape of the first matrix in the Plot """
        return self.matrices[0].shape

    def release_sheetviews(self):
        """
        Delete any Sheet.sheet_view_dict entries used by this plot, under
        the assumption that this Plot is the only object that references
        the SheetView on the Sheet with that dictionary key.
        """
        for each in self.channels:
            # Case 2 is the only time a Sheet holds a SheetView that needs
            # to be deleted.
            if isinstance(each,str) or isinstance(each,tuple):
                if self.source is None:
                    self.warning('Plot Channel-type requires a Sheet, but None Sheet passed to Plot() object.')
                    self.warning('channels = ' + str(self.channels) + ' type(self.source) = ' + str(type(self.source)))
                else:
                    self.source.release_sheet_view(each)

    def plot(self):
        """
        Get the SheetViews requested from each channel passed in at
        creation, combines them as necessary, and generates the
        Histograms.  plot() is here to allow dynamic creation of plots,
        even after the creation of the Plot object.  See the input
        parameters to the Plot object constructor.

        Returns: self (Plot)
            Important features of this object are:
                self.matrices: Triple of 2D Matrices mapping to the
                    R/G/B channels.
                self.histograms: List of Histogram objects associated with
                    the matrices is self.matrices.
                self.view_info: Name information that can be used to create
                    labels and filenames.
        """

        self.debug('plot() channels = ' + str(self.channels) + \
                   'self.source = ' + str(self.source) + \
                   'type(self.source) = ' + str(type(self.source)))

        self.channel_views = []
        self.matrices = []
        #  Convert what is in the channels into SheetViews if not already
        for each in self.channels:

            # It's possible for Plots to be accidentally passed in a
            # list of SheetViews, but it should be avoided.  A warning
            # will be displayed and the first one in the list will be
            # used.
            if isinstance(each,list) and len(each) > 0:
                if len(each) > 1:
                    self.warning('Plot Channel contains multiple entries, only plotting first: ' + str(each))
                    each = each[0]

            # Case 1: Simple SheetView with the view already embedded.
            #         No need for a Sheet.
            if isinstance(each,SheetView):
                self.channel_views.append(each)
                self.view_info = each.view_info

            # Case 2: Entry is a string, or tuple that will be used as a
            #         key in the Sheet dictionary.
            elif isinstance(each,str) or isinstance(each,tuple):
                if self.source is None:
                    self.warning('Plot Channel-type requires a Sheet, '+ \
                                 'but None Sheet passed to Plot() object.')
                    self.warning('channels = ' + str(self.channels) + \
                                 ' type(self.source) = ' + \
                                 str(type(self.source)))
                    self.channel_views.append(None)
                    self.name = 'Undefined'
                else:
                    sv = self.source.sheet_view(each)
                    if sv==None:
                        self.debug('No sheet view named ' + each + ' in Sheet ' + self.source.name)
                        self.channel_views.append(None)
                    else:
                        self.channel_views.append(sv)
                        #self.view_info = sv.view_info

                        ### JCALERT ! This is an hack so that the problem of displaying the right
                        ### name under each map in activity and orientation map panel is solved
                        ### It has to be checked than it will work in all cases
                        ### or more restructuring of the code is required
                        ### (i.e. handling of the SheetView and/or PlotGroupTemplate by PlotGroup/PlotEngine/Plot)
                        self.view_info['src_name']= self.source.name + '\n' + self.name
                        self.view_info['view_type']= self.name

            # Case 3: Channel entry is None.  Pass along.
            elif each == None:
                self.channel_views.append(each)

            # Case 4: Undefined.
            else:
                self.warning(each, 'not a String, Tuple, SheetView, or None')

        ### JABALERT! Need to document what this code does.
        shape = (0,0)
        self.debug('self.channel_views = ' + str(self.channel_views))
        for each in self.channel_views:
            if each != None:
                view_matrix = each.view()
                self.matrices.append(view_matrix[0]) # Discards boundingbox
                shape = view_matrix[0].shape
            else:
                self.matrices.append(None)

        # By this point, self.matrices should be a triple of 2D
        # matrices trimmed and ready to go to the caller of this
        # function ... after a possible conversion to a different
        # palette form.
        if self.plot_type == HSV:
            # Do the HSV-->RGB conversion, assume the caller will be
            # displaying the plot as an RGB.

            s,h,c = self.matrices

            zero=zeros(shape,Float)
            one=ones(shape,Float)

            ### JABHACKALERT Need to extend for white background; assumes black

            # No plot; in the future should probably not be a warning
            if (s==None and c==None and h==None):
                self.debug('Skipping empty plot.')
                return None

            # Determine appropriate defaults for each matrix
            if s is None: s=one # Treat as full strength by default
            if c is None: c=one # Treat as full confidence by default
            if h is None: # No color -- should be changed to drop down to COLORMAP plot.
                h=zero
                c=zero

            if self.normalize and max(s.flat) > 0:
                s = divide(s,float(max(s.flat)))

            hue=h
            sat=c
            val=s

            if max(hue.flat) > 1 or max(sat.flat) > 1 or max(val.flat) > 1:
                self.cropped = True
                #self.warning('Plot: HSVMap inputs exceed 1. Clipping to 1.0')
                if max(hue.flat) > 0: hue = MLab.clip(hue,0.0,1.0)
                if max(sat.flat) > 0: sat = MLab.clip(sat,0.0,1.0)
                if max(val.flat) > 0: val = MLab.clip(val,0.0,1.0)
            else:
                self.cropped = False

            self.matrices = matrix_hsv_to_rgb(hue,sat,val)

        elif self.plot_type == COLORMAP:
            # Don't delete anything, maybe they want position #3, but
            # do warn them if they have more than one channel
            # requested.
            #
            # COLORMAP NOW GENERATES ONLY A GRAYSCALE IMAGE. WORK
            # NEEDS TO BE DONE TO CHANGE THIS TO A SINGLE-CHANNEL
            # MATRIX THAT TRANSLATES THROUGH A PALETTE OF 256 POSSIBLE
            # COLORS THROUGH THE USE OF ColorMap and Palette.  WILL
            # NEED TO PROBABLY WORK WITH PlotEngine SINCE IT CREATES
            # THE FINAL IMAGES.
            single_map = [each for each in self.matrices if each]
            if len(single_map) > 1:
                self.warning('More than one channel requested for ' + \
                             'single-channel colormap')
            if single_map:
                max_map = max(single_map[0].flat)
                if self.normalize and max_map > 0:
                    single_map[0] = single_map[0] / max_map
                self.matrices = (single_map[0], single_map[0], single_map[0])

        elif self.plot_type == RGB:
            # Replace any Nones with a matrix of size 'shape' full of values.
            if self.background == BLACK_BACKGROUND:
                self.matrices = tuple([each or zeros(shape,Float)
                                      for each in self.matrices])
            else:
                self.matrices = tuple([each or ones(shape,Float)
                                      for each in self.matrices])

            # Otherwise do nothing, we're already in the RGB form.  Possibly
            # change the order to make it match the Lissom order, OR
            # ADD BLACK_BACKGROUND, WHITE_BACKGROUND COLOR SHIFTS.

        else:
            self.warning('Unrecognized plot type')

        # Construct a list of Histogram objects from the matrices that
        # have been created and are waiting to go.  PROBABLY HAVE TO
        # CHANGE ONCE HISTOGRAM DOES SOMETHING.
        self.histograms = [Histogram(each) for each in self.matrices if each]

        return self

<commit_msg>Added temporary comments and alert for future file re-organization.<commit_after>"""
Base Plot environment that manipulates SheetViews and has Histogram information

A plot contains information about one or more SheetViews that it combines
to create a single matrix that can be displayed as a bitmap.  Associated with
this single (constructed) box of information are multiple histograms that
are stored within the base plot object.

The plot objects are clustered together within a PlotGroup Class which
arranges multiple plots into a single figure on the screen.

The PlotEngine class uses PlotTemplate objects, embedded within
PlotGroupTemplate objects to create the Plots that will generate the
bitmaps that will be displayed on screen or saved to disk.  A
PlotTemplate is essentially a dictionary of keyed information,
containing sheet names, or channel settings.

Kinds of PlotTemplates:
    SHC Plots:
        Keys:
            Strength   - SheetView dictionary key
            Hue        - SheetView dictionary key
            Confidence - SheetView dictionary key
    Unit Weights Plots:
        Keys:
            Location   - (x,y) tuple
            Sheet_name - Name of sheet to pull unit weights
    Projection Plots:
        Keys:
            Density    - Density to plot projection weights
            Projection_name - Name of projection to plot.  Looks at
                .src sheet_view_dict

$Id$
"""
__version__='$Revision$'
### JABHACKALERT!
###
### The code in this file has not yet been reviewed, and may need
### substantial changes.

import sys
import types

from Numeric import zeros, ones, Float, divide

from topo.base.topoobject import TopoObject
from topo.base.utils import flatten
from topo.base.sheetview import *
from bitmap import matrix_hsv_to_rgb, WHITE_BACKGROUND, BLACK_BACKGROUND
from histogram import Histogram
from topo.base.parameter import Dynamic
from Numeric import array
import palette as palette
import MLab

# Types of plots that Plot knows how to create from input matrices.
RGB = 'RGB'
HSV = 'HSV'
SHC = 'HSV'  # For now treat as an HSV, but reorder the channels.
COLORMAP = 'COLORMAP'

class PlotTemplate(TopoObject):
    """
    Container class for a plot object definition.  This is separate
    from a Plot object since it defines how to create a Plot object
    and should be contained within a PlotGroupTemplate.  The
    PlotEngine will create the requested plot type given the template
    definition.  The templates are used so that standard plot types
    can be redefined at the users convenience.

    For example, 'Activity' maps are defined as:
    activity_template = PlotTemplate({'Strength'   : 'Activity',
                                      'Hue'        : None,
                                      'Confidence' : None})
    """

    def __init__(self, channels=None,**params):
        super(PlotTemplate,self).__init__(**params)
        #self.background = Dynamic(default=background)
        self.channels = channels

### JABHACKALERT!  This class should accept SheetViews, with no
### reference to anything of type Sheet, and no plotting of anything
### else.  It is ridiculously too difficult to understand and maintain
### in its current form.  Any operation that depends on Sheet, such as
### looking up SheetViews by name, or releasing SheetViews, should be
### moved out of it.  PlotTemplate might be able to do such lookup,
### because it's called for every Sheet, but if so it should always
### map consisently from a name string to a SheetView, without trying
### to handle all these special cases.  These classes are trying to be
### way too smart; they should simply plot whatever they are given.
class Plot(TopoObject):
    """
    Class that can construct a single bitmap plot from one or more
    SheetViews, optionally including a Histogram.  The bitmap is just
    stored for future use, e.g. as part of a PlotGroup of related
    plots displayed within one GUI window.
    """
    background = Dynamic(default=BLACK_BACKGROUND)
    palette_ = Dynamic(default=palette.Monochrome)

    ### JCALERT! We should pass a parameter sheet_view_list instead of passing it in the channel.

    def __init__(self, (channel_1, channel_2, channel_3), plot_type, sheet=None,
                 normalize=False, **params):
        """
        channel_1, channel_2, and channel_3 all have one of two types:
        It is either a key that points to a stored SheetView on the
        variable sheet, or
        It is a SheetView object.

        plot_type has three options: HSV, COLORMAP, or RGB.  HSV plots
        will be converted to an RGB matrix triple.  COLORMAPs take in
        a single channel, since each pixel should be a single value,
        that should be selected from a palette.  RGB plots are
        three-channel plots that are not mixed together like HSV,
        since it is assumed the GUI display will be in RGB.

        sheet gives the object that the three channels will request
        SheetViews from if a String has been passed in.  Valid options
        are: None, Single Sheet, or None.
        TODO: TRIPLE OF SHEETS, ONE FOR EACH CHANNEL, IS CURRENTLY NOT
        IMPLEMENTED.  CURRENT IMPLEMENTATION REQUIRES THAT ALL
        CHANNELS DERIVE FROM THE SAME SHEET.  SHOULD EXTEND
        self.source SO THAT DIFFERENT SHEET INPUTS ARE POSSIBLE
        (7/2005)

        If each of the channels are preconstructed SheetViews, there
        is no reason to pass in the sheet, and therefore it can be
        left blank.

        Named parameter 'name' is being inherited from TopoObject

        WHAT ABOUT BOUNDINGREGIONS?
        """
        super(Plot,self).__init__(**params)

        ### JCALERT! What about if sheet is None?
	self.source = sheet

        self.channels = (channel_1, channel_2, channel_3)

	### Maybe we can get rid of plot_type
        self.plot_type = plot_type
        self.view_info = {}
        self.cropped = False
        self.histograms = []
        self.channel_views = []
        self.matrices = []         # Will hold 3 2D matrices.
        self.normalize = normalize

    ### JCALERT! Is it really a useful and meaningful function?
    def shape(self):
        """ Return the shape of the first matrix in the Plot """
        return self.matrices[0].shape

    ### JCALERT! This function should go in another place but not in plot. (I think)
    def release_sheetviews(self):
        """
        Delete any Sheet.sheet_view_dict entries used by this plot, under
        the assumption that this Plot is the only object that references
        the SheetView on the Sheet with that dictionary key.
        """
        for each in self.channels:
            # Case 2 is the only time a Sheet holds a SheetView that needs
            # to be deleted.
            if isinstance(each,str) or isinstance(each,tuple):
                if self.source is None:
                    self.warning('Plot Channel-type requires a Sheet, but None Sheet passed to Plot() object.')
                    self.warning('channels = ' + str(self.channels) + ' type(self.source) = ' + str(type(self.source)))
                else:
                    self.source.release_sheet_view(each)

    ### JCALERT! This function ought to be simplified, and divided in smaller functions.
    def plot(self):
        """
        Get the SheetViews requested from each channel passed in at
        creation, combines them as necessary, and generates the
        Histograms.  plot() is here to allow dynamic creation of plots,
        even after the creation of the Plot object.  See the input
        parameters to the Plot object constructor.

        Returns: self (Plot)
            Important features of this object are:
                self.matrices: Triple of 2D Matrices mapping to the
                    R/G/B channels.
                self.histograms: List of Histogram objects associated with
                    the matrices is self.matrices.
                self.view_info: Name information that can be used to create
                    labels and filenames.
        """

        self.debug('plot() channels = ' + str(self.channels) + \
                   'self.source = ' + str(self.source) + \
                   'type(self.source) = ' + str(type(self.source)))

        ### JCALERT! I think that can go (already defined above!)
        ### but I do not know why, it lead to a bug with the testpattern window
        ### This has to be fixed.
	self.channel_views = []
        self.matrices = []

	### JCALERT! Here it would be changed: maybe two different classes:
	### main difference so far is if we pass the sheet_view_list or a sheet
	### the channels can be used in all cases to define which plot we want

	### or: first we do the work for the list of sheet_views, and then if there is a sheet
        ### we do it as well.

        #  Convert what is in the channels into SheetViews if not already
        for each in self.channels:

            # It's possible for Plots to be accidentally passed in a
            # list of SheetViews, but it should be avoided.  A warning
            # will be displayed and the first one in the list will be
            # used.
            if isinstance(each,list) and len(each) > 0:
                if len(each) > 1:
                    self.warning('Plot Channel contains multiple entries, only plotting first: ' + str(each))
                    each = each[0]

            # Case 1: Simple SheetView with the view already embedded.
            #         No need for a Sheet.
            if isinstance(each,SheetView):
                self.channel_views.append(each)
                self.view_info = each.view_info

            # Case 2: Entry is a string, or tuple that will be used as a
            #         key in the Sheet dictionary.
            elif isinstance(each,str) or isinstance(each,tuple):
                if self.source is None:
                    self.warning('Plot Channel-type requires a Sheet, '+ \
                                 'but None Sheet passed to Plot() object.')
                    self.warning('channels = ' + str(self.channels) + \
                                 ' type(self.source) = ' + \
                                 str(type(self.source)))
                    self.channel_views.append(None)
                    self.name = 'Undefined'
                else:
                    sv = self.source.sheet_view(each)
                    if sv==None:
                        self.debug('No sheet view named ' + each + ' in Sheet ' + self.source.name)
                        self.channel_views.append(None)
                    else:
                        self.channel_views.append(sv)
                        #self.view_info = sv.view_info

                        ### JCALERT ! This is an hack so that the problem of displaying the right
                        ### name under each map in activity and orientation map panel is solved
                        ### It has to be checked than it will work in all cases
                        ### or more restructuring of the code is required
                        ### (i.e. handling of the SheetView and/or PlotGroupTemplate by PlotGroup/PlotEngine/Plot)
                        self.view_info['src_name']= self.source.name + '\n' + self.name
                        self.view_info['view_type']= self.name

            # Case 3: Channel entry is None.  Pass along.
            elif each == None:
                self.channel_views.append(each)

            # Case 4: Undefined.
            else:
                self.warning(each, 'not a String, Tuple, SheetView, or None')

        ### JABALERT! Need to document what this code does.
        shape = (0,0)
        self.debug('self.channel_views = ' + str(self.channel_views))
        for each in self.channel_views:
            if each != None:
                view_matrix = each.view()
                self.matrices.append(view_matrix[0]) # Discards boundingbox
                shape = view_matrix[0].shape
            else:
                self.matrices.append(None)

        # By this point, self.matrices should be a triple of 2D
        # matrices trimmed and ready to go to the caller of this
        # function ... after a possible conversion to a different
        # palette form.
        if self.plot_type == HSV:
            # Do the HSV-->RGB conversion, assume the caller will be
            # displaying the plot as an RGB.

            s,h,c = self.matrices

            zero=zeros(shape,Float)
            one=ones(shape,Float)

            ### JABHACKALERT Need to extend for white background; assumes black

            # No plot; in the future should probably not be a warning
            if (s==None and c==None and h==None):
                self.debug('Skipping empty plot.')
                return None

            # Determine appropriate defaults for each matrix
            if s is None: s=one # Treat as full strength by default
            if c is None: c=one # Treat as full confidence by default
            if h is None: # No color -- should be changed to drop down to COLORMAP plot.
                h=zero
                c=zero

            if self.normalize and max(s.flat) > 0:
                s = divide(s,float(max(s.flat)))

            hue=h
            sat=c
            val=s

            if max(hue.flat) > 1 or max(sat.flat) > 1 or max(val.flat) > 1:
                self.cropped = True
                #self.warning('Plot: HSVMap inputs exceed 1. Clipping to 1.0')
                if max(hue.flat) > 0: hue = MLab.clip(hue,0.0,1.0)
                if max(sat.flat) > 0: sat = MLab.clip(sat,0.0,1.0)
                if max(val.flat) > 0: val = MLab.clip(val,0.0,1.0)
            else:
                self.cropped = False

            self.matrices = matrix_hsv_to_rgb(hue,sat,val)

        elif self.plot_type == COLORMAP:
            # Don't delete anything, maybe they want position #3, but
            # do warn them if they have more than one channel
            # requested.
            #
            # COLORMAP NOW GENERATES ONLY A GRAYSCALE IMAGE. WORK
            # NEEDS TO BE DONE TO CHANGE THIS TO A SINGLE-CHANNEL
            # MATRIX THAT TRANSLATES THROUGH A PALETTE OF 256 POSSIBLE
            # COLORS THROUGH THE USE OF ColorMap and Palette.  WILL
            # NEED TO PROBABLY WORK WITH PlotEngine SINCE IT CREATES
            # THE FINAL IMAGES.
            single_map = [each for each in self.matrices if each]
            if len(single_map) > 1:
                self.warning('More than one channel requested for ' + \
                             'single-channel colormap')
            if single_map:
                max_map = max(single_map[0].flat)
                if self.normalize and max_map > 0:
                    single_map[0] = single_map[0] / max_map
                self.matrices = (single_map[0], single_map[0], single_map[0])

        elif self.plot_type == RGB:
            # Replace any Nones with a matrix of size 'shape' full of values.
            if self.background == BLACK_BACKGROUND:
                self.matrices = tuple([each or zeros(shape,Float)
                                      for each in self.matrices])
            else:
                self.matrices = tuple([each or ones(shape,Float)
                                      for each in self.matrices])

            # Otherwise do nothing, we're already in the RGB form.  Possibly
            # change the order to make it match the Lissom order, OR
            # ADD BLACK_BACKGROUND, WHITE_BACKGROUND COLOR SHIFTS.

        else:
            self.warning('Unrecognized plot type')

        # Construct a list of Histogram objects from the matrices that
        # have been created and are waiting to go.  PROBABLY HAVE TO
        # CHANGE ONCE HISTOGRAM DOES SOMETHING.
        self.histograms = [Histogram(each) for each in self.matrices if each]

        return self

<|endoftext|>


<commit_before>"""
Base Plot environment that manipulates SheetViews and has Histogram information

A plot contains information about one or more SheetViews that it combines
to create a single matrix that can be displayed as a bitmap.  Associated with
this single (constructed) box of information are multiple histograms that
are stored within the base plot object.

The plot objects are clustered together within a PlotGroup Class which
arranges multiple plots into a single figure on the screen.

The PlotEngine class uses PlotTemplate objects, embedded within
PlotGroupTemplate objects to create the Plots that will generate the
bitmaps that will be displayed on screen or saved to disk.  A
PlotTemplate is essentially a dictionary of keyed information,
containing sheet names, or channel settings.

Kinds of PlotTemplates:
    SHC Plots:
        Keys:
            Strength   - SheetView dictionary key
            Hue        - SheetView dictionary key
            Confidence - SheetView dictionary key
    Unit Weights Plots:
        Keys:
            Location   - (x,y) tuple
            Sheet_name - Name of sheet to pull unit weights
    Projection Plots:
        Keys:
            Density    - Density to plot projection weights
            Projection_name - Name of projection to plot.  Looks at
                .src sheet_view_dict

$Id$
"""
__version__='$Revision$'
### JABHACKALERT!
###
### The code in this file has not yet been reviewed, and may need
### substantial changes.

import sys
import types

from Numeric import zeros, ones, Float, divide

from topo.base.topoobject import TopoObject
from topo.base.utils import flatten
from topo.base.sheetview import *
from bitmap import matrix_hsv_to_rgb, WHITE_BACKGROUND, BLACK_BACKGROUND
from histogram import Histogram
from topo.base.parameter import Dynamic
from Numeric import array
import palette as palette
import MLab

# Types of plots that Plot knows how to create from input matrices.
RGB = 'RGB'
HSV = 'HSV'
SHC = 'HSV'  # For now treat as an HSV, but reorder the channels.
COLORMAP = 'COLORMAP'

class PlotTemplate(TopoObject):
    """
    Container class for a plot object definition.  This is separate
    from a Plot object since it defines how to create a Plot object
    and should be contained within a PlotGroupTemplate.  The
    PlotEngine will create the requested plot type given the template
    definition.  The templates are used so that standard plot types
    can be redefined at the users convenience.

    For example, 'Activity' maps are defined as:
    activity_template = PlotTemplate({'Strength'   : 'Activity',
                                      'Hue'        : None,
                                      'Confidence' : None})
    """

    def __init__(self, channels=None,**params):
        super(PlotTemplate,self).__init__(**params)
        #self.background = Dynamic(default=background)
        self.channels = channels

### JABHACKALERT!  This class should accept SheetViews, with no
### reference to anything of type Sheet, and no plotting of anything
### else.  It is ridiculously too difficult to understand and maintain
### in its current form.  Any operation that depends on Sheet, such as
### looking up SheetViews by name, or releasing SheetViews, should be
### moved out of it.  PlotTemplate might be able to do such lookup,
### because it's called for every Sheet, but if so it should always
### map consisently from a name string to a SheetView, without trying
### to handle all these special cases.  These classes are trying to be
### way too smart; they should simply plot whatever they are given.
class Plot(TopoObject):
    """
    Class that can construct a single bitmap plot from one or more
    SheetViews, optionally including a Histogram.  The bitmap is just
    stored for future use, e.g. as part of a PlotGroup of related
    plots displayed within one GUI window.
    """
    background = Dynamic(default=BLACK_BACKGROUND)
    palette_ = Dynamic(default=palette.Monochrome)

    def __init__(self, (channel_1, channel_2, channel_3), plot_type, sheet=None,
                 normalize=False, **params):
        """
        channel_1, channel_2, and channel_3 all have one of two types:
        It is either a key that points to a stored SheetView on the
        variable sheet, or
        It is a SheetView object.

        plot_type has three options: HSV, COLORMAP, or RGB.  HSV plots
        will be converted to an RGB matrix triple.  COLORMAPs take in
        a single channel, since each pixel should be a single value,
        that should be selected from a palette.  RGB plots are
        three-channel plots that are not mixed together like HSV,
        since it is assumed the GUI display will be in RGB.

        sheet gives the object that the three channels will request
        SheetViews from if a String has been passed in.  Valid options
        are: None, Single Sheet, or None.
        TODO: TRIPLE OF SHEETS, ONE FOR EACH CHANNEL, IS CURRENTLY NOT
        IMPLEMENTED.  CURRENT IMPLEMENTATION REQUIRES THAT ALL
        CHANNELS DERIVE FROM THE SAME SHEET.  SHOULD EXTEND
        self.source SO THAT DIFFERENT SHEET INPUTS ARE POSSIBLE
        (7/2005)

        If each of the channels are preconstructed SheetViews, there
        is no reason to pass in the sheet, and therefore it can be
        left blank.

        Named parameter 'name' is being inherited from TopoObject

        WHAT ABOUT BOUNDINGREGIONS?
        """
        super(Plot,self).__init__(**params)

        self.source = sheet

        self.channels = (channel_1, channel_2, channel_3)
        self.plot_type = plot_type
        self.view_info = {}
        self.cropped = False
        self.histograms = []
        self.channel_views = []
        self.matrices = []         # Will hold 3 2D matrices.
        self.normalize = normalize

    def shape(self):
        """ Return the shape of the first matrix in the Plot """
        return self.matrices[0].shape

    def release_sheetviews(self):
        """
        Delete any Sheet.sheet_view_dict entries used by this plot, under
        the assumption that this Plot is the only object that references
        the SheetView on the Sheet with that dictionary key.
        """
        for each in self.channels:
            # Case 2 is the only time a Sheet holds a SheetView that needs
            # to be deleted.
            if isinstance(each,str) or isinstance(each,tuple):
                if self.source is None:
                    self.warning('Plot Channel-type requires a Sheet, but None Sheet passed to Plot() object.')
                    self.warning('channels = ' + str(self.channels) + ' type(self.source) = ' + str(type(self.source)))
                else:
                    self.source.release_sheet_view(each)

    def plot(self):
        """
        Get the SheetViews requested from each channel passed in at
        creation, combines them as necessary, and generates the
        Histograms.  plot() is here to allow dynamic creation of plots,
        even after the creation of the Plot object.  See the input
        parameters to the Plot object constructor.

        Returns: self (Plot)
            Important features of this object are:
                self.matrices: Triple of 2D Matrices mapping to the
                    R/G/B channels.
                self.histograms: List of Histogram objects associated with
                    the matrices is self.matrices.
                self.view_info: Name information that can be used to create
                    labels and filenames.
        """

        self.debug('plot() channels = ' + str(self.channels) + \
                   'self.source = ' + str(self.source) + \
                   'type(self.source) = ' + str(type(self.source)))

        self.channel_views = []
        self.matrices = []
        #  Convert what is in the channels into SheetViews if not already
        for each in self.channels:

            # It's possible for Plots to be accidentally passed in a
            # list of SheetViews, but it should be avoided.  A warning
            # will be displayed and the first one in the list will be
            # used.
            if isinstance(each,list) and len(each) > 0:
                if len(each) > 1:
                    self.warning('Plot Channel contains multiple entries, only plotting first: ' + str(each))
                    each = each[0]

            # Case 1: Simple SheetView with the view already embedded.
            #         No need for a Sheet.
            if isinstance(each,SheetView):
                self.channel_views.append(each)
                self.view_info = each.view_info

            # Case 2: Entry is a string, or tuple that will be used as a
            #         key in the Sheet dictionary.
            elif isinstance(each,str) or isinstance(each,tuple):
                if self.source is None:
                    self.warning('Plot Channel-type requires a Sheet, '+ \
                                 'but None Sheet passed to Plot() object.')
                    self.warning('channels = ' + str(self.channels) + \
                                 ' type(self.source) = ' + \
                                 str(type(self.source)))
                    self.channel_views.append(None)
                    self.name = 'Undefined'
                else:
                    sv = self.source.sheet_view(each)
                    if sv==None:
                        self.debug('No sheet view named ' + each + ' in Sheet ' + self.source.name)
                        self.channel_views.append(None)
                    else:
                        self.channel_views.append(sv)
                        #self.view_info = sv.view_info

                        ### JCALERT ! This is an hack so that the problem of displaying the right
                        ### name under each map in activity and orientation map panel is solved
                        ### It has to be checked than it will work in all cases
                        ### or more restructuring of the code is required
                        ### (i.e. handling of the SheetView and/or PlotGroupTemplate by PlotGroup/PlotEngine/Plot)
                        self.view_info['src_name']= self.source.name + '\n' + self.name
                        self.view_info['view_type']= self.name
                        #print self.view_info

            # Case 3: Channel entry is None.  Pass along.
            elif each == None:
                self.channel_views.append(each)

            # Case 4: Undefined.
            else:
                self.warning(each, 'not a String, Tuple, SheetView, or None')

        ### JABALERT! Need to document what this code does.
        shape = (0,0)
        self.debug('self.channel_views = ' + str(self.channel_views))
        for each in self.channel_views:
            if each != None:
                view_matrix = each.view()
                self.matrices.append(view_matrix[0]) # Discards boundingbox
                shape = view_matrix[0].shape
            else:
                self.matrices.append(None)

        # By this point, self.matrices should be a triple of 2D
        # matrices trimmed and ready to go to the caller of this
        # function ... after a possible conversion to a different
        # palette form.
        if self.plot_type == HSV:
            # Do the HSV-->RGB conversion, assume the caller will be
            # displaying the plot as an RGB.

            s,h,c = self.matrices

            zero=zeros(shape,Float)
            one=ones(shape,Float)

            ### JABHACKALERT Need to extend for white background; assumes black

            # No plot; in the future should probably not be a warning
            if (s==None and c==None and h==None):
                self.debug('Skipping empty plot.')
                return None

            # Determine appropriate defaults for each matrix
            if s is None: s=one # Treat as full strength by default
            if c is None: c=one # Treat as full confidence by default
            if h is None: # No color -- should be changed to drop down to COLORMAP plot.
                h=zero
                c=zero

            if self.normalize and max(s.flat) > 0:
                s = divide(s,float(max(s.flat)))

            hue=h
            sat=c
            val=s

            if max(hue.flat) > 1 or max(sat.flat) > 1 or max(val.flat) > 1:
                self.cropped = True
                #self.warning('Plot: HSVMap inputs exceed 1. Clipping to 1.0')
                if max(hue.flat) > 0: hue = MLab.clip(hue,0.0,1.0)
                if max(sat.flat) > 0: sat = MLab.clip(sat,0.0,1.0)
                if max(val.flat) > 0: val = MLab.clip(val,0.0,1.0)
            else:
                self.cropped = False

            self.matrices = matrix_hsv_to_rgb(hue,sat,val)

        elif self.plot_type == COLORMAP:
            # Don't delete anything, maybe they want position #3, but
            # do warn them if they have more than one channel
            # requested.
            #
            # COLORMAP NOW GENERATES ONLY A GRAYSCALE IMAGE. WORK
            # NEEDS TO BE DONE TO CHANGE THIS TO A SINGLE-CHANNEL
            # MATRIX THAT TRANSLATES THROUGH A PALETTE OF 256 POSSIBLE
            # COLORS THROUGH THE USE OF ColorMap and Palette.  WILL
            # NEED TO PROBABLY WORK WITH PlotEngine SINCE IT CREATES
            # THE FINAL IMAGES.
            single_map = [each for each in self.matrices if each]
            if len(single_map) > 1:
                self.warning('More than one channel requested for ' + \
                             'single-channel colormap')
            if single_map:
                max_map = max(single_map[0].flat)
                if self.normalize and max_map > 0:
                    single_map[0] = single_map[0] / max_map
                self.matrices = (single_map[0], single_map[0], single_map[0])

        elif self.plot_type == RGB:
            # Replace any Nones with a matrix of size 'shape' full of values.
            if self.background == BLACK_BACKGROUND:
                self.matrices = tuple([each or zeros(shape,Float)
                                      for each in self.matrices])
            else:
                self.matrices = tuple([each or ones(shape,Float)
                                      for each in self.matrices])

            # Otherwise do nothing, we're already in the RGB form.  Possibly
            # change the order to make it match the Lissom order, OR
            # ADD BLACK_BACKGROUND, WHITE_BACKGROUND COLOR SHIFTS.

        else:
            self.warning('Unrecognized plot type')

        # Construct a list of Histogram objects from the matrices that
        # have been created and are waiting to go.  PROBABLY HAVE TO
        # CHANGE ONCE HISTOGRAM DOES SOMETHING.
        self.histograms = [Histogram(each) for each in self.matrices if each]

        return self

<commit_msg>Erased an unused comment<commit_after>"""
Base Plot environment that manipulates SheetViews and has Histogram information

A plot contains information about one or more SheetViews that it combines
to create a single matrix that can be displayed as a bitmap.  Associated with
this single (constructed) box of information are multiple histograms that
are stored within the base plot object.

The plot objects are clustered together within a PlotGroup Class which
arranges multiple plots into a single figure on the screen.

The PlotEngine class uses PlotTemplate objects, embedded within
PlotGroupTemplate objects to create the Plots that will generate the
bitmaps that will be displayed on screen or saved to disk.  A
PlotTemplate is essentially a dictionary of keyed information,
containing sheet names, or channel settings.

Kinds of PlotTemplates:
    SHC Plots:
        Keys:
            Strength   - SheetView dictionary key
            Hue        - SheetView dictionary key
            Confidence - SheetView dictionary key
    Unit Weights Plots:
        Keys:
            Location   - (x,y) tuple
            Sheet_name - Name of sheet to pull unit weights
    Projection Plots:
        Keys:
            Density    - Density to plot projection weights
            Projection_name - Name of projection to plot.  Looks at
                .src sheet_view_dict

$Id$
"""
__version__='$Revision$'
### JABHACKALERT!
###
### The code in this file has not yet been reviewed, and may need
### substantial changes.

import sys
import types

from Numeric import zeros, ones, Float, divide

from topo.base.topoobject import TopoObject
from topo.base.utils import flatten
from topo.base.sheetview import *
from bitmap import matrix_hsv_to_rgb, WHITE_BACKGROUND, BLACK_BACKGROUND
from histogram import Histogram
from topo.base.parameter import Dynamic
from Numeric import array
import palette as palette
import MLab

# Types of plots that Plot knows how to create from input matrices.
RGB = 'RGB'
HSV = 'HSV'
SHC = 'HSV'  # For now treat as an HSV, but reorder the channels.
COLORMAP = 'COLORMAP'

class PlotTemplate(TopoObject):
    """
    Container class for a plot object definition.  This is separate
    from a Plot object since it defines how to create a Plot object
    and should be contained within a PlotGroupTemplate.  The
    PlotEngine will create the requested plot type given the template
    definition.  The templates are used so that standard plot types
    can be redefined at the users convenience.

    For example, 'Activity' maps are defined as:
    activity_template = PlotTemplate({'Strength'   : 'Activity',
                                      'Hue'        : None,
                                      'Confidence' : None})
    """

    def __init__(self, channels=None,**params):
        super(PlotTemplate,self).__init__(**params)
        #self.background = Dynamic(default=background)
        self.channels = channels

### JABHACKALERT!  This class should accept SheetViews, with no
### reference to anything of type Sheet, and no plotting of anything
### else.  It is ridiculously too difficult to understand and maintain
### in its current form.  Any operation that depends on Sheet, such as
### looking up SheetViews by name, or releasing SheetViews, should be
### moved out of it.  PlotTemplate might be able to do such lookup,
### because it's called for every Sheet, but if so it should always
### map consisently from a name string to a SheetView, without trying
### to handle all these special cases.  These classes are trying to be
### way too smart; they should simply plot whatever they are given.
class Plot(TopoObject):
    """
    Class that can construct a single bitmap plot from one or more
    SheetViews, optionally including a Histogram.  The bitmap is just
    stored for future use, e.g. as part of a PlotGroup of related
    plots displayed within one GUI window.
    """
    background = Dynamic(default=BLACK_BACKGROUND)
    palette_ = Dynamic(default=palette.Monochrome)

    def __init__(self, (channel_1, channel_2, channel_3), plot_type, sheet=None,
                 normalize=False, **params):
        """
        channel_1, channel_2, and channel_3 all have one of two types:
        It is either a key that points to a stored SheetView on the
        variable sheet, or
        It is a SheetView object.

        plot_type has three options: HSV, COLORMAP, or RGB.  HSV plots
        will be converted to an RGB matrix triple.  COLORMAPs take in
        a single channel, since each pixel should be a single value,
        that should be selected from a palette.  RGB plots are
        three-channel plots that are not mixed together like HSV,
        since it is assumed the GUI display will be in RGB.

        sheet gives the object that the three channels will request
        SheetViews from if a String has been passed in.  Valid options
        are: None, Single Sheet, or None.
        TODO: TRIPLE OF SHEETS, ONE FOR EACH CHANNEL, IS CURRENTLY NOT
        IMPLEMENTED.  CURRENT IMPLEMENTATION REQUIRES THAT ALL
        CHANNELS DERIVE FROM THE SAME SHEET.  SHOULD EXTEND
        self.source SO THAT DIFFERENT SHEET INPUTS ARE POSSIBLE
        (7/2005)

        If each of the channels are preconstructed SheetViews, there
        is no reason to pass in the sheet, and therefore it can be
        left blank.

        Named parameter 'name' is being inherited from TopoObject

        WHAT ABOUT BOUNDINGREGIONS?
        """
        super(Plot,self).__init__(**params)

        self.source = sheet

        self.channels = (channel_1, channel_2, channel_3)
        self.plot_type = plot_type
        self.view_info = {}
        self.cropped = False
        self.histograms = []
        self.channel_views = []
        self.matrices = []         # Will hold 3 2D matrices.
        self.normalize = normalize

    def shape(self):
        """ Return the shape of the first matrix in the Plot """
        return self.matrices[0].shape

    def release_sheetviews(self):
        """
        Delete any Sheet.sheet_view_dict entries used by this plot, under
        the assumption that this Plot is the only object that references
        the SheetView on the Sheet with that dictionary key.
        """
        for each in self.channels:
            # Case 2 is the only time a Sheet holds a SheetView that needs
            # to be deleted.
            if isinstance(each,str) or isinstance(each,tuple):
                if self.source is None:
                    self.warning('Plot Channel-type requires a Sheet, but None Sheet passed to Plot() object.')
                    self.warning('channels = ' + str(self.channels) + ' type(self.source) = ' + str(type(self.source)))
                else:
                    self.source.release_sheet_view(each)

    def plot(self):
        """
        Get the SheetViews requested from each channel passed in at
        creation, combines them as necessary, and generates the
        Histograms.  plot() is here to allow dynamic creation of plots,
        even after the creation of the Plot object.  See the input
        parameters to the Plot object constructor.

        Returns: self (Plot)
            Important features of this object are:
                self.matrices: Triple of 2D Matrices mapping to the
                    R/G/B channels.
                self.histograms: List of Histogram objects associated with
                    the matrices is self.matrices.
                self.view_info: Name information that can be used to create
                    labels and filenames.
        """

        self.debug('plot() channels = ' + str(self.channels) + \
                   'self.source = ' + str(self.source) + \
                   'type(self.source) = ' + str(type(self.source)))

        self.channel_views = []
        self.matrices = []
        #  Convert what is in the channels into SheetViews if not already
        for each in self.channels:

            # It's possible for Plots to be accidentally passed in a
            # list of SheetViews, but it should be avoided.  A warning
            # will be displayed and the first one in the list will be
            # used.
            if isinstance(each,list) and len(each) > 0:
                if len(each) > 1:
                    self.warning('Plot Channel contains multiple entries, only plotting first: ' + str(each))
                    each = each[0]

            # Case 1: Simple SheetView with the view already embedded.
            #         No need for a Sheet.
            if isinstance(each,SheetView):
                self.channel_views.append(each)
                self.view_info = each.view_info

            # Case 2: Entry is a string, or tuple that will be used as a
            #         key in the Sheet dictionary.
            elif isinstance(each,str) or isinstance(each,tuple):
                if self.source is None:
                    self.warning('Plot Channel-type requires a Sheet, '+ \
                                 'but None Sheet passed to Plot() object.')
                    self.warning('channels = ' + str(self.channels) + \
                                 ' type(self.source) = ' + \
                                 str(type(self.source)))
                    self.channel_views.append(None)
                    self.name = 'Undefined'
                else:
                    sv = self.source.sheet_view(each)
                    if sv==None:
                        self.debug('No sheet view named ' + each + ' in Sheet ' + self.source.name)
                        self.channel_views.append(None)
                    else:
                        self.channel_views.append(sv)
                        #self.view_info = sv.view_info

                        ### JCALERT ! This is an hack so that the problem of displaying the right
                        ### name under each map in activity and orientation map panel is solved
                        ### It has to be checked than it will work in all cases
                        ### or more restructuring of the code is required
                        ### (i.e. handling of the SheetView and/or PlotGroupTemplate by PlotGroup/PlotEngine/Plot)
                        self.view_info['src_name']= self.source.name + '\n' + self.name
                        self.view_info['view_type']= self.name

            # Case 3: Channel entry is None.  Pass along.
            elif each == None:
                self.channel_views.append(each)

            # Case 4: Undefined.
            else:
                self.warning(each, 'not a String, Tuple, SheetView, or None')

        ### JABALERT! Need to document what this code does.
        shape = (0,0)
        self.debug('self.channel_views = ' + str(self.channel_views))
        for each in self.channel_views:
            if each != None:
                view_matrix = each.view()
                self.matrices.append(view_matrix[0]) # Discards boundingbox
                shape = view_matrix[0].shape
            else:
                self.matrices.append(None)

        # By this point, self.matrices should be a triple of 2D
        # matrices trimmed and ready to go to the caller of this
        # function ... after a possible conversion to a different
        # palette form.
        if self.plot_type == HSV:
            # Do the HSV-->RGB conversion, assume the caller will be
            # displaying the plot as an RGB.

            s,h,c = self.matrices

            zero=zeros(shape,Float)
            one=ones(shape,Float)

            ### JABHACKALERT Need to extend for white background; assumes black

            # No plot; in the future should probably not be a warning
            if (s==None and c==None and h==None):
                self.debug('Skipping empty plot.')
                return None

            # Determine appropriate defaults for each matrix
            if s is None: s=one # Treat as full strength by default
            if c is None: c=one # Treat as full confidence by default
            if h is None: # No color -- should be changed to drop down to COLORMAP plot.
                h=zero
                c=zero

            if self.normalize and max(s.flat) > 0:
                s = divide(s,float(max(s.flat)))

            hue=h
            sat=c
            val=s

            if max(hue.flat) > 1 or max(sat.flat) > 1 or max(val.flat) > 1:
                self.cropped = True
                #self.warning('Plot: HSVMap inputs exceed 1. Clipping to 1.0')
                if max(hue.flat) > 0: hue = MLab.clip(hue,0.0,1.0)
                if max(sat.flat) > 0: sat = MLab.clip(sat,0.0,1.0)
                if max(val.flat) > 0: val = MLab.clip(val,0.0,1.0)
            else:
                self.cropped = False

            self.matrices = matrix_hsv_to_rgb(hue,sat,val)

        elif self.plot_type == COLORMAP:
            # Don't delete anything, maybe they want position #3, but
            # do warn them if they have more than one channel
            # requested.
            #
            # COLORMAP NOW GENERATES ONLY A GRAYSCALE IMAGE. WORK
            # NEEDS TO BE DONE TO CHANGE THIS TO A SINGLE-CHANNEL
            # MATRIX THAT TRANSLATES THROUGH A PALETTE OF 256 POSSIBLE
            # COLORS THROUGH THE USE OF ColorMap and Palette.  WILL
            # NEED TO PROBABLY WORK WITH PlotEngine SINCE IT CREATES
            # THE FINAL IMAGES.
            single_map = [each for each in self.matrices if each]
            if len(single_map) > 1:
                self.warning('More than one channel requested for ' + \
                             'single-channel colormap')
            if single_map:
                max_map = max(single_map[0].flat)
                if self.normalize and max_map > 0:
                    single_map[0] = single_map[0] / max_map
                self.matrices = (single_map[0], single_map[0], single_map[0])

        elif self.plot_type == RGB:
            # Replace any Nones with a matrix of size 'shape' full of values.
            if self.background == BLACK_BACKGROUND:
                self.matrices = tuple([each or zeros(shape,Float)
                                      for each in self.matrices])
            else:
                self.matrices = tuple([each or ones(shape,Float)
                                      for each in self.matrices])

            # Otherwise do nothing, we're already in the RGB form.  Possibly
            # change the order to make it match the Lissom order, OR
            # ADD BLACK_BACKGROUND, WHITE_BACKGROUND COLOR SHIFTS.

        else:
            self.warning('Unrecognized plot type')

        # Construct a list of Histogram objects from the matrices that
        # have been created and are waiting to go.  PROBABLY HAVE TO
        # CHANGE ONCE HISTOGRAM DOES SOMETHING.
        self.histograms = [Histogram(each) for each in self.matrices if each]

        return self

<|endoftext|>


<commit_before>import json, subprocess, curses, sys, os, glob
from menu import cmenu
yes = set(["yes", "y"])
DEFAULT_JSON = subprocess.call("wget ----", shell=True)
JSON_LOCATION = ""

def bulkInstall():
	json_data = open(DEFAULT_JSON)
	data = json.load(json_data)
	root = data["install"]
	for item in root:
		subprocess.call("sudo add-apt-repository " + item["repo"] + " -y", shell=True)
	subprocess.call("sudo apt-get update", shell=True)
	for item in root:
		if item["command"]:
			subprocess.call(item["command"], shell=True)
		subprocess.call("sudo apt-get install -y " + item["app"], shell=True)

def aptInstall(program, repo, command):
	global yes
	if not repo and not command:
		subprocess.call("sudo apt-get install " + program, shell=True)
	elif repo:
		choice = raw_input("Do you want to add ppa " + repo + " (Required to install " + program +") (y/n)").lower()
		if choice in yes:
			subprocess.call("sudo add-apt-repository " + repo + " && sudo apt-get update", shell=True)
			subprocess.call("sudo apt-get install " + program, shell=True)
		else:
			print("Cancelled install of " + program)
	elif command:
		subprocess.call(command, shell=True)
		subprocess.call("sudo apt-get update && sudo apt-get install " + program, shell=True)

def promptInstall():
	clear()
	json_data = open(DEFAULT_JSON)
	data = json.load(json_data)
	root = data["install"]
	for item in root:
		choice = raw_input("Do you want to install " + item["app"] + " (y/n)").lower()
		if choice in yes:
			aptInstall(item["app"], item["repo"], item["command"])
	json_data.close()

def exit():
	#clear()
	sys.exit(1)
def clear():
	subprocess.call("clear", shell=True)

def selectJSON():
	global DEFAULT_JSON
	num = -1
	clear()
	for file in os.listdir(JSON_LOCATION):
		if file.endswith(".json"):
			files = glob.glob(JSON_LOCATION+"/*.json")
			num += 1
			print("["+str(num) + "] " + file)
	choice = raw_input("Choose one [0-"+str(num)+"] ")
	print(files[int(choice)])
	DEFAULT_JSON = files[int(choice)]

def main():
	home = os.getenv("HOME")
	if not os.path.exists(home + "/.instpakg"):
		os.makedirs(home + "/.instpakg")
		global JSON_LOCATION
		JSON_LOCATION = home + "/.instpakg"
		print(JSON_LOCATION + "  "+ DEFAULT_JSON)
		subprocess.call("cp " + DEFAULT_JSON + " " + JSON_LOCATION, shell=True)
	else:
		global JSON_LOCATION
		JSON_LOCATION = home + "/.instpakg"
	try:
		list = [{ "Install software": promptInstall }, {"Bulk Software Install": bulkInstall}, {"Select JSON file": selectJSON}, {"Exit": exit}]
		menu = cmenu(list, "InstPakg Menu")
		menu.display()
	except SystemExit:
		pass
	else:
		menu.cleanup()

<commit_msg>On first use or if config folder does not exist, download template JSON file.<commit_after>import json, subprocess, curses, sys, os, glob
from menu import cmenu
yes = set(["yes", "y"])
DEFAULT_JSON = "DEFAULT.json"
JSON_LOCATION = ""

def bulkInstall():
	json_data = open(DEFAULT_JSON)
	data = json.load(json_data)
	root = data["install"]
	for item in root:
		subprocess.call("sudo add-apt-repository " + item["repo"] + " -y", shell=True)
	subprocess.call("sudo apt-get update", shell=True)
	for item in root:
		if item["command"]:
			subprocess.call(item["command"], shell=True)
		subprocess.call("sudo apt-get install -y " + item["app"], shell=True)

def aptInstall(program, repo, command):
	global yes
	if not repo and not command:
		subprocess.call("sudo apt-get install " + program, shell=True)
	elif repo:
		choice = raw_input("Do you want to add ppa " + repo + " (Required to install " + program +") (y/n)").lower()
		if choice in yes:
			subprocess.call("sudo add-apt-repository " + repo + " && sudo apt-get update", shell=True)
			subprocess.call("sudo apt-get install " + program, shell=True)
		else:
			print("Cancelled install of " + program)
	elif command:
		subprocess.call(command, shell=True)
		subprocess.call("sudo apt-get update && sudo apt-get install " + program, shell=True)

def promptInstall():
	clear()
	json_data = open(DEFAULT_JSON)
	data = json.load(json_data)
	root = data["install"]
	for item in root:
		choice = raw_input("Do you want to install " + item["app"] + " (y/n)").lower()
		if choice in yes:
			aptInstall(item["app"], item["repo"], item["command"])
	json_data.close()

def exit():
	#clear()
	sys.exit(1)
def clear():
	subprocess.call("clear", shell=True)

def selectJSON():
	global DEFAULT_JSON
	num = -1
	clear()
	for file in os.listdir(JSON_LOCATION):
		if file.endswith(".json"):
			files = glob.glob(JSON_LOCATION+"/*.json")
			num += 1
			print("["+str(num) + "] " + file)
	choice = raw_input("Choose one [0-"+str(num)+"] ")
	print(files[int(choice)])
	DEFAULT_JSON = files[int(choice)]

def main():
	home = os.getenv("HOME")
	if not os.path.exists(home + "/.instpakg"):
		os.makedirs(home + "/.instpakg")
		global JSON_LOCATION
		JSON_LOCATION = home + "/.instpakg"
		print(JSON_LOCATION + "  "+ DEFAULT_JSON)
		subprocess.call("wget -O " + JSON_LOCATION + " https://github.com/Mozzo1000/InstPakg/blob/master/source/DEFAULT.json", shell=True)
	else:
		global JSON_LOCATION
		JSON_LOCATION = home + "/.instpakg"
	try:
		list = [{ "Install software": promptInstall }, {"Bulk Software Install": bulkInstall}, {"Select JSON file": selectJSON}, {"Exit": exit}]
		menu = cmenu(list, "InstPakg Menu")
		menu.display()
	except SystemExit:
		pass
	else:
		menu.cleanup()

<|endoftext|>