Example #1
0
class Slice(_BaseSceneComponent):
    """Position and color information for a slice in the Scene"""
    _defaults = {'uid': 'slice'}
    position_a = properties.Vector3(
        'Position of first plane',
        default=lambda: [0, 0, 0],
    )
    position_b = properties.Vector3(
        'Position of second plane; only required in section mode',
        default=[0, 0, 0],
        required=False,
    )
    normal = properties.Vector3(
        'Normal vector to the slice plane',
        default=lambda: [1., 0, 0],
    )
    mode = properties.StringChoice(
        'Slice mode',
        ['inactive', 'normal', 'reversed', 'section'],
        descriptions={
            'inactive': 'Slice is currently disabled',
            'normal': 'Standard single slice slicing mode',
            'reversed': 'Single slice with swapped normal',
            'section': 'Mode with two parallel sliceplanes'
        },
        default='inactive',
    )
    color_a = properties.Color(
        'Color of first slice',
        default=[0, 120, 255],
        serializer=spatial.mappings.to_hex,
        deserializer=spatial.mappings.from_hex,
    )
    color_b = properties.Color(
        'Color of second slice; only required in section mode',
        default=[253, 102, 0],
        required=False,
        serializer=spatial.mappings.to_hex,
        deserializer=spatial.mappings.from_hex,
    )

    @properties.validator
    def _validate_section(self):
        """Validate that position_b and color_b are set in section mode"""
        if (self.mode == 'section'
                and (self.position_b is None or self.color_b is None)):
            raise properties.ValidationError(
                message='Section mode requires position_b and color_b',
                reason='invalid',
                prop='mode',
                instance=self,
            )
Example #2
0
class ProjectElement(ContentModel):
    """Base ProjectElement class for OMF file

    ProjectElement subclasses must define their mesh.
    ProjectElements include PointSet, LineSet, Surface, and Volume
    """
    data = properties.List(
        'Data defined on the element',
        prop=ProjectElementData,
        required=False,
        default=list,
    )
    color = properties.Color('Solid color', default='random')
    geometry = None

    @properties.validator
    def _validate_data(self):
        """Check if element is built correctly"""
        assert self.geometry is not None, 'ProjectElement must have a mesh'
        for i, dat in enumerate(self.data):
            if dat.location not in self.geometry._valid_locations:  #pylint: disable=protected-access
                raise ValueError(
                    'Invalid location {loc} - valid values: {locs}'.format(
                        loc=dat.location,
                        locs=', '.join(self.geometry._valid_locations)  #pylint: disable=protected-access
                    ))
            valid_length = self.geometry.location_length(dat.location)
            if len(dat.array) != valid_length:
                raise ValueError(
                    'data[{index}] length {datalen} does not match '
                    '{loc} length {meshlen}'.format(index=i,
                                                    datalen=len(dat.array),
                                                    loc=dat.location,
                                                    meshlen=valid_length))
        return True
Example #3
0
class ColorArray(ScalarArray):
    """Shared array of Colors"""
    array = properties.List(
        'Shared array of Colors',
        prop=properties.Color(''),
        default=list,
    )
Example #4
0
 class HasOptionalUnion(properties.HasProperties):
     mybc = properties.Union(
         'union of bool or color',
         props=[properties.Bool(''),
                properties.Color('')],
         required=False,
     )
Example #5
0
 class ValidateColorThenString(properties.HasProperties):
     values = properties.Union(
         'Values',
         props=[
             properties.List('', properties.Color('')),
             properties.List('', properties.String('')),
         ],
     )
Example #6
0
 class HasOptPropsUnion(properties.HasProperties):
     mybc = properties.Union(
         'union of bool or color',
         props=[
             properties.Bool('', required=False),
             properties.Color('', required=False),
         ],
         required=True,
     )
Example #7
0
class _BaseAnnotation(_BaseSlideComponent):
    """Base class for all annotations"""
    uid = properties.String('Locally unique ID from client', required=False)
    position = properties.List(
        'Location of annotation on slide plane',
        properties.Float(''),
        min_length=2,
        max_length=2,
    )
    color = properties.Color('Annotation color')
Example #8
0
class ColorOptions(Options):
    """Options related to resource display color"""
    color = properties.Color(doc='Solid color',
                             default='random',
                             required=False)
    opacity = properties.Float(doc='Opacity',
                               default=1.,
                               min=0.,
                               max=1.,
                               required=False)
class OptionsSurfaceColor(OptionsColor):
    """Options for displayed surface color

    Identical to :class:`lfview.resources.spatial.options.OptionsColor`
    except it also includes solid back color.
    """

    back = properties.Color(
        'Back color of the surface, only used if data is unspecified',
        required=False,
        serializer=to_hex,
        deserializer=from_hex,
    )
class OptionsColor(_BaseOptionsData):
    """Options for displayed color

    You may specify a single solid color value for the entire Element or
    a variable colormap using data and mapping that evaluates to RGB color
    """

    value = properties.Color(
        'Single color value, only used if data is unspecified',
        required=False,
        serializer=to_hex,
        deserializer=from_hex,
    )
Example #11
0
    def test_color(self):

        class ColorOpts(properties.HasProperties):
            mycolor = properties.Color('My color')

        col = ColorOpts(mycolor='red')
        assert col.mycolor == (255, 0, 0)
        col.mycolor = 'darkred'
        assert col.mycolor == (139, 0, 0)
        col.mycolor = '#FFF'
        assert col.mycolor == (255, 255, 255)
        col.mycolor = [50, 50, 50]
        assert col.mycolor == (50, 50, 50)
        col.mycolor = 'random'
        assert len(col.mycolor) == 3
        with self.assertRaises(ValueError):
            col.mycolor = 'SunburnRed'
        with self.assertRaises(ValueError):
            col.mycolor = '#00112233'
        with self.assertRaises(ValueError):
            col.mycolor = '#CDEFGH'
        with self.assertRaises(ValueError):
            col.mycolor = 5
        with self.assertRaises(ValueError):
            col.mycolor = (1.3, 5.3, 100.6)
        with self.assertRaises(ValueError):
            col.mycolor = [5, 100]
        with self.assertRaises(ValueError):
            col.mycolor = [-10, 0, 0]

        col.mycolor = 'red'
        self.assertEqual(col.serialize(include_class=False),
                         {'mycolor': [255, 0, 0]})
        assert ColorOpts.deserialize(
            {'mycolor': [0, 10, 20]}
        ).mycolor == (0, 10, 20)

        assert properties.Color('').equal((0, 10, 20), (0, 10, 20))
        assert not properties.Color('').equal((0, 10, 20), [0, 10, 20])
Example #12
0
class TestModel(BaseModel):
    a_key = properties.String('A key')
    sub_instance = properties.Instance(
        'Sub Instance',
        instance_class=BaseModel,
    )
    list = properties.List(
        'List',
        prop=properties.Instance('List Instance', instance_class=BaseModel),
    )
    list_string = properties.List(
        'List of Strings',
        prop=properties.String('String'),
    )
    date = properties.DateTime('DateTime')
    color = properties.Color('Color')
    not_a_field = True
Example #13
0
class Tag(BaseModel):

    tag = properties.String(
        'The tag value.',
        required=True,
    )
    slug = properties.String(
        'Slugified version of the tag value.',
    )
    count = properties.Integer(
        'The number of times the tag is used.',
    )
    color = properties.Color(
        'The tag color.',
    )
    created_at = properties.DateTime(
        'UTC time when this tag was created.',
    )
    modified_at = properties.DateTime(
        'UTC time when this tag was modified.',
    )
Example #14
0
 class HasColorTuple(properties.HasProperties):
     ccc = properties.Tuple('tuple of colors',
                            properties.Color(''),
                            min_length=2,
                            max_length=2)
Example #15
0
 class HasColor(properties.HasProperties):
     col = properties.Color('a color', default='random')
Example #16
0
class DataArray(BaseData):
    """Data array with unique values at every point in the mesh

    .. note:

        DataArray custom colormap is currently unsupported on
        steno3d.com
    """
    _resource_class = 'array'
    array = properties.Array(
        doc='Data, unique values at every point in the mesh',
        shape=('*', ),
        dtype=(float, int),
        serializer=array_serializer,
        deserializer=array_download(('*', ), (float, int)),
    )

    order = properties.StringChoice(
        doc='Data array order, for data on grid meshes',
        choices={
            'c': ('C-STYLE', 'NUMPY', 'ROW-MAJOR', 'ROW'),
            'f': ('FORTRAN', 'MATLAB', 'COLUMN-MAJOR', 'COLUMN', 'COL')
        },
        default='c',
    )
    colormap = properties.List(
        doc='Colormap applied to data range or categories',
        prop=properties.Color(''),
        min_length=1,
        max_length=256,
        required=False,
    )

    def __init__(self, array=None, **kwargs):
        super(DataArray, self).__init__(**kwargs)
        if array is not None:
            self.array = array

    def _nbytes(self, arr=None):
        if arr is None or (isinstance(arr, string_types) and arr == 'array'):
            arr = self.array
        if isinstance(arr, np.ndarray):
            return arr.astype('f4').nbytes
        raise ValueError('DataArray cannot calculate the number of '
                         'bytes of {}'.format(arr))

    @properties.observer('array')
    def _reject_large_files(self, change):
        self._validate_file_size(change['name'], change['value'])

    @properties.validator
    def _validate_array(self):
        self._validate_file_size('array', self.array)
        return True

    def _get_dirty_data(self, force=False):
        datadict = super(DataArray, self)._get_dirty_data(force)
        dirty = self._dirty_props
        if 'order' in dirty or force:
            datadict['order'] = self.order
        if self.colormap and ('colormap' in dirty or force):
            datadict['colormap'] = dumps(self.colormap)
        return datadict

    def _get_dirty_files(self, force=False):
        files = super(DataArray, self)._get_dirty_files(force)
        dirty = self._dirty_props
        if 'array' in dirty or force:
            files['array'] = self._props['array'].serialize(self.array)
        return files

    @classmethod
    def _build_from_json(cls, json, **kwargs):
        data = DataArray(title=kwargs['title'],
                         description=kwargs['description'],
                         order=json['order'],
                         array=cls._props['array'].deserialize(
                             json['array'],
                             input_dtype=json.get('arrayType', None),
                         ))
        if json.get('colormap'):
            data.colormap = json['colormap']
        return data

    @classmethod
    def _build_from_omf(cls, omf_data):
        data = dict(location='N' if omf_data.location == 'vertices' else 'CC',
                    data=DataArray(title=omf_data.name,
                                   description=omf_data.description,
                                   array=omf_data.array.array))
        if omf_data.colormap:
            dlims = np.linspace(np.nanmin(omf_data.array.array),
                                np.nanmax(omf_data.array.array), 257)
            dlims = (dlims[:-1] + dlims[1:]) / 2
            omf_dlims = np.linspace(
                omf_data.colormap.limits[0],
                omf_data.colormap.limits[1],
                len(omf_data.colormap.gradient.array) + 1,
            )
            omf_dlims = (omf_dlims[:-1] + omf_dlims[1:]) / 2
            colormap = []
            for dval in dlims:
                colormap += [
                    omf_data.colormap.gradient.array[np.argmin(
                        np.abs(omf_dlims - dval))]
                ]
            data['data'].colormap = colormap
        return data

    def _to_omf(self):
        if self.order != 'c':
            raise ValueError('OMF data must be "c" order')
        import omf
        data = omf.ScalarData(
            name=self.title or '',
            description=self.description or '',
            array=omf.ScalarArray(self.array),
        )
        if self.colormap:
            data.colormap = omf.ScalarColormap(
                gradient=omf.ColorArray(self.colormap, ),
                limits=[np.min(self.array),
                        np.max(self.array)],
            )
        return data
Example #17
0
 class HasBoolColor(properties.HasProperties):
     mybc = properties.Union(
         'union of bool or color',
         props=[properties.Bool(''),
                properties.Color('')])
Example #18
0
 class HasColorSet(properties.HasProperties):
     ccc = properties.Set('set of colors',
                          properties.Color(''),
                          observe_mutations=om)
Example #19
0
 class ColorOpts(properties.HasProperties):
     mycolor = properties.Color('My color')
class MappingDiscrete(_BaseDataMapping):
    """Mapping of continuous data to discrete intervals

    These mappings are used to categorize continuous numeric data.
    Define the limits of the intervals by specifying end points, and
    specify if the end points are inclusive in the lower or upper bucket.
    Then assign values to each interval.

    .. code::

      #       values
      #
      #         --                          x - - - - ->
      #
      #         --                  x - - - o
      #
      #         --     <- - - - - - o
      #
      #                <------------|--------|------------> data
      #                             end_points
    """

    SUB_TYPE = 'discrete'

    values = properties.Union(
        'Values corresponding to intervals',
        props=[
            properties.List(
                '',
                properties.Color('', serializer=to_hex, deserializer=from_hex),
                max_length=256,
            ),
            properties.List('', properties.Float(''), max_length=256),
            properties.List(
                '',
                ShortString('', max_length=300),
                max_length=256,
            ),
        ],
    )
    end_points = properties.List(
        'Data end values of discrete intervals; these also correspond to '
        'the start of the next interval. First start and final end are '
        'fixed at -inf and inf, respectively.',
        prop=properties.Float(''),
        max_length=255,
    )
    end_inclusive = properties.List(
        'True if corresponding end is inclusive for lower range and false if '
        'it is inclusive for upper range; must be specified for each interval',
        prop=properties.Boolean('', cast=True),
        max_length=255,
    )
    visibility = properties.List(
        'True if interval is visible; must be specified for each interval',
        prop=properties.Boolean('', cast=True),
        max_length=256,
    )

    @properties.validator
    def _validate_lengths(self):
        """Validate lengths of values/end_points/end_inclusive/visibility"""
        if len(self.values) != len(self.visibility):
            raise properties.ValidationError(
                message='values and visibility must be equal length',
                reason='invalid',
                prop='visibility',
                instance=self,
            )
        if len(self.values) != len(self.end_points) + 1:
            raise properties.ValidationError(
                message='values must be one longer than end points',
                reason='invalid',
                prop='end_points',
                instance=self,
            )
        if len(self.values) != len(self.end_inclusive) + 1:
            raise properties.ValidationError(
                message='values must be one longer than end inclusive',
                reason='invalid',
                prop='end_inclusive',
                instance=self,
            )

    @properties.validator('end_points')
    def _validate_increasing(self, change):
        """Ensure end_points are increasing"""
        if change['value'] is properties.undefined:
            return
        diffs = np.array(change['value'][1:]) - np.array(change['value'][:-1])
        if not np.all(diffs >= 0):
            raise properties.ValidationError(
                message='end points must not decrease: {}'.format(
                    change['value']
                ),
                reason='invalid',
                prop='end_points',
                instance=self,
            )
Example #21
0
 class HasColorList(properties.HasProperties):
     ccc = properties.List('list of colors',
                           properties.Color(''),
                           observe_mutations=om)
class MappingCategory(_BaseDataMapping):
    """Mapping of integer index values to categories

    These mappings are used to define categories on
    :class:`spatial.resources.spatial.data.DataCategory` as
    well as color and other visual aspects. Data array values
    correspond to indices which then map to the values. If an
    array value is not present in indices, it is assumed there
    is no data at that location.

    .. code::

      #       values
      #
      #         --                          x
      #
      #         --            x
      #
      #         --       x
      #
      #                  |    |             |
      #                        indices
    """
    SUB_TYPE = 'category'

    values = properties.Union(
        'Values corresponding to indices',
        props=[
            properties.List(
                '',
                properties.Color('', serializer=to_hex, deserializer=from_hex),
                max_length=256,
            ),
            properties.List('', properties.Float(''), max_length=256),
            properties.List(
                '',
                ShortString('', max_length=300),
                max_length=256,
            ),
        ],
    )
    indices = properties.List(
        'Array indices for values',
        properties.Integer('', min=0),
        max_length=256,
    )
    visibility = properties.List(
        'True if category is visible',
        prop=properties.Boolean('', cast=True),
        max_length=256,
    )

    @properties.validator
    def _validate_lengths(self):
        """Validate lengths of values/indices/visibility"""
        if len(self.values) != len(self.indices):
            raise properties.ValidationError(
                message='values and indices must be equal length',
                reason='invalid',
                prop='indices',
                instance=self,
            )
        if len(self.values) != len(self.visibility):
            raise properties.ValidationError(
                message='values and visibility must be equal length',
                reason='invalid',
                prop='visibility',
                instance=self,
            )

    @properties.validator('indices')
    def _validate_indices_unique(self, change):
        """Ensure indices are unique"""
        if change['value'] is properties.undefined:
            return
        if len(change['value']) != len(set(change['value'])):
            raise properties.ValidationError(
                message='indices must be unique: {}'.format(change['value']),
                reason='invalid',
                prop='indices',
                instance=self,
            )

    def to_omf(self, index_map):
        self.validate()
        new_values = []
        if not self.values or isinstance(self.values[0], float):
            nan_value = np.nan
        elif isinstance(self.values[0], string_types):
            nan_value = ''
        else:
            nan_value = [255, 255, 255]
        for ind in index_map:
            try:
                new_values.append(self.values[self.indices.index(ind)])
            except ValueError:
                new_values.append(nan_value)
        omf_legend = omf.Legend(
            name=self.name or '',
            description=self.description or '',
            values=new_values,
        )
        return omf_legend
Example #23
0
 class HasFunnyDict(properties.HasProperties):
     mydict = properties.Dictionary('my dict',
                                    key_prop=properties.Color(''),
                                    value_prop=HasInt,
                                    observe_mutations=om)
Example #24
0
 class HasColorTuple(properties.HasProperties):
     ccc = properties.Tuple('tuple of colors', properties.Color(''))