<commit_before>import copy
from itertools import product

import numpy as np
from matplotlib import pyplot as plt

import param

from ..core.options import Store
from ..core import CompositeOverlay, Element
from ..core import traversal
from ..core.util import match_spec, max_range
from ..element.raster import HeatMap, Image, Raster, RGB, HSV
from .element import ElementPlot, OverlayPlot
from .plot import Plot, GridPlot

class RasterPlot(ElementPlot):

    colorbar = param.Boolean(default=False, doc="""
        Whether to add a colorbar to the plot.""")

    situate_axes = param.Boolean(default=False, doc="""
        Whether to situate the image relative to other plots. """)

    show_values = param.Boolean(default=False, doc="""
        Whether to annotate each pixel with its value.""")

    style_opts = ['alpha', 'cmap', 'interpolation', 'visible',
                  'filterrad', 'origin', 'clims']

    def __init__(self, *args, **kwargs):
        super(RasterPlot, self).__init__(*args, **kwargs)
        if self.map.type == Raster:
            self.invert_yaxis = True

    def get_extents(self, view, ranges):
        extents = super(RasterPlot, self).get_extents(view, ranges)
        if self.situate_axes:
            return extents
        else:
            if isinstance(view, Image):
                return view.bounds.lbrt()
            else:
                return view.extents

    def __call__(self, ranges=None):
        view = self.map.last
        axis = self.handles['axis']

        ranges = self.compute_ranges(self.map, self.keys[-1], ranges)
        ranges = match_spec(view, ranges)
        xdim, ydim = view.key_dimensions
        if isinstance(view, Image):
            l, b, r, t = view.bounds.lbrt()
        else:
            (l, r), (b, t) = ranges[xdim.name], ranges[ydim.name]

        xticks, yticks = self._compute_ticks(view, ranges)

        opts = self.style[self.cyclic_index]
        data = view.data
        clims = opts.pop('clims', None)
        if view.depth != 1:
            opts.pop('cmap', None)

        if isinstance(view, RGB):
            data = view.rgb.data
        elif isinstance(view, HeatMap):
            data = view.data
            data = np.ma.array(data, mask=np.logical_not(np.isfinite(data)))
            cmap_name = opts.pop('cmap', None)
            cmap = copy.copy(plt.cm.get_cmap('gray' if cmap_name is None else cmap_name))
            cmap.set_bad('w', 1.)
            opts['cmap'] = cmap
            l, b, r, t = view.extents
        elif type(view) == Raster:
            b, t = t, b
            r+=1; b+=1

        im = axis.imshow(data, extent=[l, r, b, t], zorder=self.zorder, **opts)
        if clims is None:
            val_dim = [d.name for d in view.value_dimensions][0]
            clims = ranges.get(val_dim)
        im.set_clim(clims)
        self.handles['im'] = im
        if self.colorbar:
            self._draw_colorbar(im)

        if isinstance(view, HeatMap):
            self.handles['axis'].set_aspect(float(r - l)/(t-b))
            self.handles['annotations'] = {}

            if self.show_values:
                self._annotate_values(view)

        return self._finalize_axis(self.keys[-1], ranges=ranges,
                                   xticks=xticks, yticks=yticks)

    def _compute_ticks(self, view, ranges):
        if isinstance(view, HeatMap):
            xdim, ydim = view.key_dimensions
            dim1_keys, dim2_keys = view.dense_keys()
            num_x, num_y = len(dim1_keys), len(dim2_keys)
            x0, y0, x1, y1 = view.extents
            xstep, ystep = ((x1-x0)/num_x, (y1-y0)/num_y)
            xpos = np.linspace(x0+xstep/2., x1-xstep/2., num_x)
            ypos = np.linspace(y0+ystep/2., y1-ystep/2., num_y)
            xlabels = [xdim.pprint_value(k) for k in dim1_keys] if xdim.formatter else dim1_keys
            ylabels = [ydim.pprint_value(k) for k in dim2_keys] if ydim.formatter else dim2_keys
            return (xpos, xlabels), (ypos, ylabels)
        else:
            return None, None

    def _annotate_values(self, view):
        axis = self.handles['axis']
        val_dim = view.value_dimensions[0]
        dim1_keys, dim2_keys = view.dense_keys()
        num_x, num_y = len(dim1_keys), len(dim2_keys)
        xstep, ystep = 1.0/num_x, 1.0/num_y
        xpos = np.linspace(xstep/2., 1.0-xstep/2., num_x)
        ypos = np.linspace(ystep/2., 1.0-ystep/2., num_y)
        coords = product(dim1_keys, dim2_keys)
        plot_coords = product(xpos, ypos)
        for plot_coord, coord in zip(plot_coords, coords):
            if isinstance(view, HeatMap):
                val = view._data.get(coord, np.NaN)
                val = val[0] if isinstance(val, tuple) else val
            else:
                val = view[coord]
            val = val_dim.type(val) if val_dim.type else val
            val = val[0] if isinstance(val, tuple) else val
            text = val_dim.pprint_value(val)
            text = '' if val is np.nan else text
            if plot_coord not in self.handles['annotations']:
                annotation = axis.annotate(text, xy=plot_coord,
                                           xycoords='axes fraction',
                                           horizontalalignment='center',
                                           verticalalignment='center')
                self.handles['annotations'][plot_coord] = annotation
            else:
                self.handles['annotations'][plot_coord].set_text(text)
        old_coords = set(self.handles['annotations'].keys()) - set(product(xpos, ypos))
        for plot_coord in old_coords:
            annotation = self.handles['annotations'].pop(plot_coord)
            annotation.remove()

    def update_handles(self, axis, view, key, ranges=None):
        im = self.handles.get('im', None)
        im.set_data(view.data)

        if isinstance(view, HeatMap) and self.show_values:
           self._annotate_values(view)
        if self.colorbar:
            self._draw_colorbar(im)

        xdim, ydim = view.key_dimensions
        if isinstance(view, Image):
            l, b, r, t = view.bounds.lbrt()
        else:
            (l, r), (b, t) = ranges[xdim.name], ranges[ydim.name]
        if type(view) == Raster:
            b, t = t, b
            r+=1; b+=1
        val_dim = [d.name for d in view.value_dimensions][0]
        im.set_clim(ranges.get(val_dim))
        im.set_extent((l, r, b, t))
        xticks, yticks = self._compute_ticks(view, ranges)
        return {'xticks': xticks, 'yticks': yticks}

class RasterGridPlot(GridPlot, OverlayPlot):
    """
    RasterGridPlot evenly spaces out plots of individual projections on
    a grid, even when they differ in size. Since this class uses a single
    axis to generate all the individual plots it is much faster than the
    equivalent using subplots.
    """

    aspect = param.Parameter(default='auto', doc="""
        Aspect ratios on RasterGridPlot should be automatically determined.""")

    border = param.Number(default=10, doc="""
        Aggregate border as a fraction of total plot size.""")

    show_title = param.Boolean(default=True)

    style_opts = ['alpha', 'cmap', 'interpolation', 'visible',
                  'filterrad', 'origin']

    def __init__(self, layout, keys=None, dimensions=None, create_axes=False, ranges=None,
                 layout_num=1, **params):
        if not keys or not dimensions:
            dimensions, keys = traversal.unique_dimkeys(layout)
        Plot.__init__(self, dimensions=dimensions, keys=keys, **params)
        self.layout = layout
        self.cyclic_index = 0
        self.zorder = 0
        self.layout_num = layout_num
        self.overlaid = False
        self.map = {}
        if layout.ndims > 1:
            xkeys, ykeys = zip(*layout.data.keys())
        else:
            xkeys = layout.keys()
            ykeys = [None]
        self._xkeys = sorted(set(xkeys))
        self._ykeys = sorted(set(ykeys))
        self._xticks, self._yticks = [], []
        self.rows, self.cols = layout.shape
        _, _, self.layout = self._create_subplots(layout, None, ranges, create_axes=False)

    def get_extents(self, view, ranges):
        width, height, _, _, _, _ = self._compute_borders(ranges)
        return (0, 0, width, height)

    def _get_frame(self, key):
        return GridPlot._get_frame(self, key)

    def __call__(self, ranges=None):
        width, height, b_w, b_h, widths, heights = self._compute_borders(ranges)

        key = self.keys[-1]
        ranges = self.compute_ranges(self.layout, key, ranges)
        self.handles['projs'] = []
        x, y = b_w, b_h
        for xidx, xkey in enumerate(self._xkeys):
            w = widths[xidx]
            for yidx, ykey in enumerate(self._ykeys):
                h = heights[yidx]
                if self.layout.ndims > 1:
                    vmap = self.layout.get((xkey, ykey), None)
                else:
                    vmap = self.layout.get(xkey, None)
                pane = vmap.select({d: val for d, val in zip(self.dimensions, key)
                                    if d in vmap.key_dimensions}).last
                if pane:
                    if issubclass(vmap.type, CompositeOverlay): pane = pane.values()[-1]
                    data = pane.data if pane else None
                else:
                    pane = vmap.last.values()[-1] if issubclass(vmap.type, CompositeOverlay) else vmap.last
                    data = pane.data
                ranges = self.compute_ranges(vmap, key, ranges)
                opts = Store.lookup_options(pane, 'style')[self.cyclic_index]
                plot = self.handles['axis'].imshow(data, extent=(x,x+w, y, y+h), **opts)
                valrange = match_spec(pane, ranges)[pane.value_dimensions[0].name]
                plot.set_clim(valrange)
                if data is None:
                    plot.set_visible(False)
                self.handles['projs'].append(plot)
                y += h + b_h
                if xidx == 0:
                    self._yticks.append(y-b_h-h/2.)
            y = b_h
            x += w + b_w
            self._xticks.append(x-b_w-w/2.)

        grid_dims = self.layout.key_dimensions
        ydim = grid_dims[1] if self.layout.ndims > 1 else None
        xticks = (self._xticks, self._process_ticklabels(self._xkeys, grid_dims[0]))
        yticks = (self._yticks, self._process_ticklabels(self._ykeys, ydim))
        ylabel = str(self.layout.key_dimensions[1]) if self.layout.ndims > 1 else ''

        return self._finalize_axis(key, ranges=ranges,
                                   title=self._format_title(key),
                                   xticks=xticks, yticks=yticks,
                                   xlabel=str(self.layout.get_dimension(0)),
                                   ylabel=ylabel)

    def update_frame(self, key, ranges=None):
        grid_values = self.layout.values()
        ranges = self.compute_ranges(self.layout, key, ranges)
        for i, plot in enumerate(self.handles['projs']):
            view = grid_values[i].get(key, None)
            if view:
                plot.set_visible(True)
                data = view.values()[0].data if isinstance(view, CompositeOverlay) else view.data
                plot.set_data(data)
            else:
                plot.set_visible(False)

        xdim = self.layout.key_dimensions[0]
        ydim = self.layout.key_dimensions[1] if self.layout.ndims > 1 else None

        self._finalize_axis(key, ranges=ranges, title=self._format_title(key),
                            xticks=(self._xticks, self._process_ticklabels(self._xkeys, xdim)),
                            yticks=(self._yticks, self._process_ticklabels(self._ykeys, ydim)))

    def _axis_labels(self, view, subplots, xlabel, ylabel, zlabel):
        xdim = self.layout.key_dimensions[0]
        ydim = self.layout.key_dimensions[1] if self.layout.ndims > 1 else None
        return xlabel if xlabel else str(xdim), ylabel if ylabel or not ydim else str(ydim), zlabel

    def _compute_borders(self, ranges):
        ndims = self.layout.ndims
        xkey, ykey = self._xkeys[0], self._ykeys[0]
        width_fn = lambda x: x.range(0)
        height_fn = lambda x: x.range(1)
        if ndims > 1:
            vert_section = self.layout[xkey, slice(None)]
        else:
            vert_section = [self.layout[xkey]]
        horz_section = self.layout[(slice(None), ykey) if ndims > 1 else slice(None)]
        height_extents = [max_range(hm.traverse(height_fn, [Element])) for hm in vert_section]
        width_extents = [max_range(hm.traverse(width_fn, [Element])) for hm in horz_section]
        widths = [extent[0]-extent[1] for extent in width_extents]
        heights = [extent[0]-extent[1] for extent in height_extents]
        width, height = np.sum(widths), np.sum(heights)
        border_width = (width/10.)/(len(widths)+1)
        border_height = (height/10.)/(len(heights)+1)
        width += width/10.
        height += height/10.

        return width, height, border_width, border_height, widths, heights

    def __len__(self):
        return max([len(self.keys), 1])

Store.registry.update({Raster: RasterPlot,
                       HeatMap: RasterPlot,
                       Image: RasterPlot,
                       RGB: RasterPlot,
                       HSV: RasterPlot})

<commit_msg>Fixes to extents handling in RasterPlot<commit_after>import copy
from itertools import product

import numpy as np
from matplotlib import pyplot as plt

import param

from ..core.options import Store
from ..core import CompositeOverlay, Element
from ..core import traversal
from ..core.util import match_spec, max_range
from ..element.raster import HeatMap, Image, Raster, RGB, HSV
from .element import ElementPlot, OverlayPlot
from .plot import Plot, GridPlot

class RasterPlot(ElementPlot):

    colorbar = param.Boolean(default=False, doc="""
        Whether to add a colorbar to the plot.""")

    situate_axes = param.Boolean(default=False, doc="""
        Whether to situate the image relative to other plots. """)

    show_values = param.Boolean(default=False, doc="""
        Whether to annotate each pixel with its value.""")

    style_opts = ['alpha', 'cmap', 'interpolation', 'visible',
                  'filterrad', 'origin', 'clims']

    def __init__(self, *args, **kwargs):
        super(RasterPlot, self).__init__(*args, **kwargs)
        if self.map.type == Raster:
            self.invert_yaxis = True

    def get_extents(self, view, ranges):
        extents = super(RasterPlot, self).get_extents(view, ranges)
        if self.situate_axes:
            return extents
        else:
            if isinstance(view, Image):
                return view.bounds.lbrt()
            else:
                return view.extents

    def __call__(self, ranges=None):
        view = self.map.last
        axis = self.handles['axis']

        ranges = self.compute_ranges(self.map, self.keys[-1], ranges)
        ranges = match_spec(view, ranges)
        xdim, ydim = view.key_dimensions

        xticks, yticks = self._compute_ticks(view, ranges)

        opts = self.style[self.cyclic_index]
        data = view.data
        clims = opts.pop('clims', None)
        if view.depth != 1:
            opts.pop('cmap', None)

        if isinstance(view, Image):
            l, b, r, t = view.bounds.lbrt()
        else:
            l, b, r, t = view.extents
            if type(view) == Raster:
                b, t = t, b

        if isinstance(view, RGB):
            data = view.rgb.data
        elif isinstance(view, HeatMap):
            data = view.data
            data = np.ma.array(data, mask=np.logical_not(np.isfinite(data)))
            cmap_name = opts.pop('cmap', None)
            cmap = copy.copy(plt.cm.get_cmap('gray' if cmap_name is None else cmap_name))
            cmap.set_bad('w', 1.)
            opts['cmap'] = cmap

        im = axis.imshow(data, extent=[l, r, b, t], zorder=self.zorder, **opts)
        if clims is None:
            val_dim = [d.name for d in view.value_dimensions][0]
            clims = ranges.get(val_dim)
        im.set_clim(clims)
        self.handles['im'] = im
        if self.colorbar:
            self._draw_colorbar(im)

        if isinstance(view, HeatMap):
            self.handles['axis'].set_aspect(float(r - l)/(t-b))
            self.handles['annotations'] = {}

            if self.show_values:
                self._annotate_values(view)

        return self._finalize_axis(self.keys[-1], ranges=ranges,
                                   xticks=xticks, yticks=yticks)

    def _compute_ticks(self, view, ranges):
        if isinstance(view, HeatMap):
            xdim, ydim = view.key_dimensions
            dim1_keys, dim2_keys = view.dense_keys()
            num_x, num_y = len(dim1_keys), len(dim2_keys)
            x0, y0, x1, y1 = view.extents
            xstep, ystep = ((x1-x0)/num_x, (y1-y0)/num_y)
            xpos = np.linspace(x0+xstep/2., x1-xstep/2., num_x)
            ypos = np.linspace(y0+ystep/2., y1-ystep/2., num_y)
            xlabels = [xdim.pprint_value(k) for k in dim1_keys] if xdim.formatter else dim1_keys
            ylabels = [ydim.pprint_value(k) for k in dim2_keys] if ydim.formatter else dim2_keys
            return (xpos, xlabels), (ypos, ylabels)
        else:
            return None, None

    def _annotate_values(self, view):
        axis = self.handles['axis']
        val_dim = view.value_dimensions[0]
        dim1_keys, dim2_keys = view.dense_keys()
        num_x, num_y = len(dim1_keys), len(dim2_keys)
        xstep, ystep = 1.0/num_x, 1.0/num_y
        xpos = np.linspace(xstep/2., 1.0-xstep/2., num_x)
        ypos = np.linspace(ystep/2., 1.0-ystep/2., num_y)
        coords = product(dim1_keys, dim2_keys)
        plot_coords = product(xpos, ypos)
        for plot_coord, coord in zip(plot_coords, coords):
            if isinstance(view, HeatMap):
                val = view._data.get(coord, np.NaN)
                val = val[0] if isinstance(val, tuple) else val
            else:
                val = view[coord]
            val = val_dim.type(val) if val_dim.type else val
            val = val[0] if isinstance(val, tuple) else val
            text = val_dim.pprint_value(val)
            text = '' if val is np.nan else text
            if plot_coord not in self.handles['annotations']:
                annotation = axis.annotate(text, xy=plot_coord,
                                           xycoords='axes fraction',
                                           horizontalalignment='center',
                                           verticalalignment='center')
                self.handles['annotations'][plot_coord] = annotation
            else:
                self.handles['annotations'][plot_coord].set_text(text)
        old_coords = set(self.handles['annotations'].keys()) - set(product(xpos, ypos))
        for plot_coord in old_coords:
            annotation = self.handles['annotations'].pop(plot_coord)
            annotation.remove()

    def update_handles(self, axis, view, key, ranges=None):
        im = self.handles.get('im', None)
        im.set_data(view.data)

        if isinstance(view, HeatMap) and self.show_values:
           self._annotate_values(view)
        if self.colorbar:
            self._draw_colorbar(im)

        xdim, ydim = view.key_dimensions
        if isinstance(view, Image):
            l, b, r, t = view.bounds.lbrt()
        else:
            l, b, r, t = view.extents
            if type(view) == Raster:
                b, t = t, b

        val_dim = [d.name for d in view.value_dimensions][0]
        im.set_clim(ranges.get(val_dim))
        im.set_extent((l, r, b, t))
        xticks, yticks = self._compute_ticks(view, ranges)
        return {'xticks': xticks, 'yticks': yticks}

class RasterGridPlot(GridPlot, OverlayPlot):
    """
    RasterGridPlot evenly spaces out plots of individual projections on
    a grid, even when they differ in size. Since this class uses a single
    axis to generate all the individual plots it is much faster than the
    equivalent using subplots.
    """

    aspect = param.Parameter(default='auto', doc="""
        Aspect ratios on RasterGridPlot should be automatically determined.""")

    border = param.Number(default=10, doc="""
        Aggregate border as a fraction of total plot size.""")

    show_title = param.Boolean(default=True)

    style_opts = ['alpha', 'cmap', 'interpolation', 'visible',
                  'filterrad', 'origin']

    def __init__(self, layout, keys=None, dimensions=None, create_axes=False, ranges=None,
                 layout_num=1, **params):
        if not keys or not dimensions:
            dimensions, keys = traversal.unique_dimkeys(layout)
        Plot.__init__(self, dimensions=dimensions, keys=keys, **params)
        self.layout = layout
        self.cyclic_index = 0
        self.zorder = 0
        self.layout_num = layout_num
        self.overlaid = False
        self.map = {}
        if layout.ndims > 1:
            xkeys, ykeys = zip(*layout.data.keys())
        else:
            xkeys = layout.keys()
            ykeys = [None]
        self._xkeys = sorted(set(xkeys))
        self._ykeys = sorted(set(ykeys))
        self._xticks, self._yticks = [], []
        self.rows, self.cols = layout.shape
        _, _, self.layout = self._create_subplots(layout, None, ranges, create_axes=False)

    def get_extents(self, view, ranges):
        width, height, _, _, _, _ = self._compute_borders(ranges)
        return (0, 0, width, height)

    def _get_frame(self, key):
        return GridPlot._get_frame(self, key)

    def __call__(self, ranges=None):
        width, height, b_w, b_h, widths, heights = self._compute_borders(ranges)

        key = self.keys[-1]
        ranges = self.compute_ranges(self.layout, key, ranges)
        self.handles['projs'] = []
        x, y = b_w, b_h
        for xidx, xkey in enumerate(self._xkeys):
            w = widths[xidx]
            for yidx, ykey in enumerate(self._ykeys):
                h = heights[yidx]
                if self.layout.ndims > 1:
                    vmap = self.layout.get((xkey, ykey), None)
                else:
                    vmap = self.layout.get(xkey, None)
                pane = vmap.select({d: val for d, val in zip(self.dimensions, key)
                                    if d in vmap.key_dimensions}).last
                if pane:
                    if issubclass(vmap.type, CompositeOverlay): pane = pane.values()[-1]
                    data = pane.data if pane else None
                else:
                    pane = vmap.last.values()[-1] if issubclass(vmap.type, CompositeOverlay) else vmap.last
                    data = pane.data
                ranges = self.compute_ranges(vmap, key, ranges)
                opts = Store.lookup_options(pane, 'style')[self.cyclic_index]
                plot = self.handles['axis'].imshow(data, extent=(x,x+w, y, y+h), **opts)
                valrange = match_spec(pane, ranges)[pane.value_dimensions[0].name]
                plot.set_clim(valrange)
                if data is None:
                    plot.set_visible(False)
                self.handles['projs'].append(plot)
                y += h + b_h
                if xidx == 0:
                    self._yticks.append(y-b_h-h/2.)
            y = b_h
            x += w + b_w
            self._xticks.append(x-b_w-w/2.)

        grid_dims = self.layout.key_dimensions
        ydim = grid_dims[1] if self.layout.ndims > 1 else None
        xticks = (self._xticks, self._process_ticklabels(self._xkeys, grid_dims[0]))
        yticks = (self._yticks, self._process_ticklabels(self._ykeys, ydim))
        ylabel = str(self.layout.key_dimensions[1]) if self.layout.ndims > 1 else ''

        return self._finalize_axis(key, ranges=ranges,
                                   title=self._format_title(key),
                                   xticks=xticks, yticks=yticks,
                                   xlabel=str(self.layout.get_dimension(0)),
                                   ylabel=ylabel)

    def update_frame(self, key, ranges=None):
        grid_values = self.layout.values()
        ranges = self.compute_ranges(self.layout, key, ranges)
        for i, plot in enumerate(self.handles['projs']):
            view = grid_values[i].get(key, None)
            if view:
                plot.set_visible(True)
                data = view.values()[0].data if isinstance(view, CompositeOverlay) else view.data
                plot.set_data(data)
            else:
                plot.set_visible(False)

        xdim = self.layout.key_dimensions[0]
        ydim = self.layout.key_dimensions[1] if self.layout.ndims > 1 else None

        self._finalize_axis(key, ranges=ranges, title=self._format_title(key),
                            xticks=(self._xticks, self._process_ticklabels(self._xkeys, xdim)),
                            yticks=(self._yticks, self._process_ticklabels(self._ykeys, ydim)))

    def _axis_labels(self, view, subplots, xlabel, ylabel, zlabel):
        xdim = self.layout.key_dimensions[0]
        ydim = self.layout.key_dimensions[1] if self.layout.ndims > 1 else None
        return xlabel if xlabel else str(xdim), ylabel if ylabel or not ydim else str(ydim), zlabel

    def _compute_borders(self, ranges):
        ndims = self.layout.ndims
        xkey, ykey = self._xkeys[0], self._ykeys[0]
        width_fn = lambda x: x.range(0)
        height_fn = lambda x: x.range(1)
        if ndims > 1:
            vert_section = self.layout[xkey, slice(None)]
        else:
            vert_section = [self.layout[xkey]]
        horz_section = self.layout[(slice(None), ykey) if ndims > 1 else slice(None)]
        height_extents = [max_range(hm.traverse(height_fn, [Element])) for hm in vert_section]
        width_extents = [max_range(hm.traverse(width_fn, [Element])) for hm in horz_section]
        widths = [extent[0]-extent[1] for extent in width_extents]
        heights = [extent[0]-extent[1] for extent in height_extents]
        width, height = np.sum(widths), np.sum(heights)
        border_width = (width/10.)/(len(widths)+1)
        border_height = (height/10.)/(len(heights)+1)
        width += width/10.
        height += height/10.

        return width, height, border_width, border_height, widths, heights

    def __len__(self):
        return max([len(self.keys), 1])

Store.registry.update({Raster: RasterPlot,
                       HeatMap: RasterPlot,
                       Image: RasterPlot,
                       RGB: RasterPlot,
                       HSV: RasterPlot})

<|endoftext|>


<commit_before>#
# Copyright 2016 The BigDL Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import os
import sys
import gc
from tempfile import NamedTemporaryFile

from pyspark.cloudpickle import print_exec
from pyspark.broadcast import Broadcast
from pyspark.broadcast import _from_id
from bigdl.nn.layer import Model

def _from_id_and_type(bid, bigdl_type):
    result = _from_id(bid)
    return ModelBroadcast(path=result._path, bigdl_type=bigdl_type)

def broadcast_model(sc, layer):
    return ModelBroadcast(sc, layer, sc._pickled_broadcast_vars)

class ModelBroadcast(Broadcast):

    def __init__(self, sc=None, layer=None, pickle_registry=None, path=None, bigdl_type="float"):
        """
        Should not be called directly by users -- use L{SparkContext.broadcast()}
        instead.
        """
        if layer is not None:
            self.bigdl_type = layer.bigdl_type
        else:
            self.bigdl_type = bigdl_type
        super(ModelBroadcast, self).__init__(sc, layer, pickle_registry, path)

    def dump(self, value, f):
        try:
            value.saveModel(f.name, over_write=True)
        except Exception as e:
            msg = "Could not serialize broadcast: %s" % e.__class__.__name__
            print_exec(sys.stderr)
            raise ValueError(msg)
        f.close()
        return f.name

    def _load(self, path):
        return Model.loadModel(path, bigdl_type=self.bigdl_type)

    @property
    def value(self):
        """ Return the broadcasted value
        """
        if not hasattr(self, "_value") and self._path is not None:
            self._value = self._load(self._path)
        return self._value

    def __reduce__(self):
        if self._jbroadcast is None:
            raise Exception("Broadcast can only be serialized in driver")
        self._pickle_registry.add(self)
        return _from_id_and_type, (self._jbroadcast.id(), self.bigdl_type)
<commit_msg>[WIP] spark 3.0 (#3054)<commit_after>#
# Copyright 2016 The BigDL Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import os
import sys
import gc
from tempfile import NamedTemporaryFile

from pyspark.broadcast import Broadcast
from pyspark.broadcast import _from_id
from bigdl.nn.layer import Model

def _from_id_and_type(bid, bigdl_type):
    result = _from_id(bid)
    return ModelBroadcast(path=result._path, bigdl_type=bigdl_type)

def broadcast_model(sc, layer):
    return ModelBroadcast(sc, layer, sc._pickled_broadcast_vars)

class ModelBroadcast(Broadcast):

    def __init__(self, sc=None, layer=None, pickle_registry=None, path=None, bigdl_type="float"):
        """
        Should not be called directly by users -- use L{SparkContext.broadcast()}
        instead.
        """
        if layer is not None:
            self.bigdl_type = layer.bigdl_type
        else:
            self.bigdl_type = bigdl_type
        super(ModelBroadcast, self).__init__(sc, layer, pickle_registry, path)

    def dump(self, value, f):
        try:
            value.saveModel(f.name, over_write=True)
        except Exception as e:
            msg = "Could not serialize broadcast: %s" % e.__class__.__name__
            if not self.sc.version.startswith("2.1"):
                from pyspark.cloudpickle import print_exec
            else:
                from pyspark.util import print_exec
            print_exec(sys.stderr)
            raise ValueError(msg)
        f.close()
        return f.name

    def _load(self, path):
        return Model.loadModel(path, bigdl_type=self.bigdl_type)

    @property
    def value(self):
        """ Return the broadcasted value
        """
        if not hasattr(self, "_value") and self._path is not None:
            self._value = self._load(self._path)
        return self._value

    def __reduce__(self):
        if self._jbroadcast is None:
            raise Exception("Broadcast can only be serialized in driver")
        self._pickle_registry.add(self)
        return _from_id_and_type, (self._jbroadcast.id(), self.bigdl_type)
<|endoftext|>


<commit_before># Copyright (c) 2009-2019 The Regents of the University of Michigan
# This file is part of the HOOMD-blue project, released under the BSD 3-Clause
# License.

import numpy
import hoomd
import hoomd.variant

def test_constant():
    """ Test construction and properties of variant.constant
    """

    a = hoomd.variant.Constant(10.0)

    assert a.value == 10.0

    a.value = 0.125

def test_constant_eval():
    a = hoomd.variant.Constant(10.0)

    for i in range(10000):
        assert a(i) == 10.0

    for i in range(10000000000, 10000010000):
        assert a(i) == 10.0

def test_custom():
    class CustomVariant(hoomd.variant.Variant):
        def __init__(self):
            hoomd.variant.Variant.__init__(self)

        def __call__(self, timestep):
            return (float(timestep)**(1 / 2))

    c = CustomVariant()

    # test that the custom variant can be called from c++

    for i in range(10000):
        assert hoomd._hoomd._test_variant_call(c, i) == float(i)**(1 / 2)

    for i in range(10000000000, 10000010000):
        assert hoomd._hoomd._test_variant_call(c, i) == float(i)**(1 / 2)

def test_ramp():
    a = hoomd.variant.Ramp(1.0, 11.0, 100, 10000)

    for i in range(100):
        assert a(i) == 1.0

    assert a(100) == 1.0
    numpy.testing.assert_allclose(a(2600), 3.5)
    numpy.testing.assert_allclose(a(5100), 6.0)
    numpy.testing.assert_allclose(a(7600), 8.5)
    assert a(10100) == 11.0

    for i in range(10000000000, 10000010000):
        assert a(i) == 11.0

def test_ramp_properties():
    a = hoomd.variant.Ramp(1.0, 11.0, 100, 10000)
    assert a.A == 1.0
    assert a.B == 11.0
    assert a.t_start == 100
    assert a.t_ramp == 10000

    a.A = 100
    assert a.A == 100.0
    assert a(0) == 100.0

    a.B = -25
    assert a.B == -25.0
    assert a(10100) == -25.0

    a.t_start = 1000
    assert a.t_start == 1000
    assert a(1000) == 100.0
    assert a(1001) < 100.0

    a.t_ramp = int(1e6)
    assert a.t_ramp == int(1e6)
    assert a(1001000) == -25.0
    assert a(1000999) > -25.0

<commit_msg>Test out of bounds ramp value<commit_after># Copyright (c) 2009-2019 The Regents of the University of Michigan
# This file is part of the HOOMD-blue project, released under the BSD 3-Clause
# License.

import numpy
import hoomd
import hoomd.variant
import pytest

def test_constant():
    """ Test construction and properties of variant.constant
    """

    a = hoomd.variant.Constant(10.0)

    assert a.value == 10.0

    a.value = 0.125

def test_constant_eval():
    a = hoomd.variant.Constant(10.0)

    for i in range(10000):
        assert a(i) == 10.0

    for i in range(10000000000, 10000010000):
        assert a(i) == 10.0

def test_custom():
    class CustomVariant(hoomd.variant.Variant):
        def __init__(self):
            hoomd.variant.Variant.__init__(self)

        def __call__(self, timestep):
            return (float(timestep)**(1 / 2))

    c = CustomVariant()

    # test that the custom variant can be called from c++

    for i in range(10000):
        assert hoomd._hoomd._test_variant_call(c, i) == float(i)**(1 / 2)

    for i in range(10000000000, 10000010000):
        assert hoomd._hoomd._test_variant_call(c, i) == float(i)**(1 / 2)

def test_ramp():
    a = hoomd.variant.Ramp(1.0, 11.0, 100, 10000)

    for i in range(100):
        assert a(i) == 1.0

    assert a(100) == 1.0
    numpy.testing.assert_allclose(a(2600), 3.5)
    numpy.testing.assert_allclose(a(5100), 6.0)
    numpy.testing.assert_allclose(a(7600), 8.5)
    assert a(10100) == 11.0

    for i in range(10000000000, 10000010000):
        assert a(i) == 11.0

def test_ramp_properties():
    a = hoomd.variant.Ramp(1.0, 11.0, 100, 10000)
    assert a.A == 1.0
    assert a.B == 11.0
    assert a.t_start == 100
    assert a.t_ramp == 10000

    a.A = 100
    assert a.A == 100.0
    assert a(0) == 100.0

    a.B = -25
    assert a.B == -25.0
    assert a(10100) == -25.0

    a.t_start = 1000
    assert a.t_start == 1000
    assert a(1000) == 100.0
    assert a(1001) < 100.0

    a.t_ramp = int(1e6)
    assert a.t_ramp == int(1e6)
    assert a(1001000) == -25.0
    assert a(1000999) > -25.0

    with pytest.raises(ValueError):
        a.t_ramp = int(2**53+1)
<|endoftext|>


<commit_before>import hsgame.cards

__author__ = 'Daniel'

from hsgame.constants import CHARACTER_CLASS, CARD_RARITY, MINION_TYPE
from hsgame.game_objects import MinionCard, Minion, SecretCard

class ManaWyrm(MinionCard):
    def __init__(self):
        super().__init__("Mana Wyrm", 1, CHARACTER_CLASS.MAGE, CARD_RARITY.COMMON)

    def create_minion(self, player):
        def increase_attack(card):
            minion.increase_attack(1)
        minion = Minion(1, 3)
        player.bind("spell_cast", increase_attack)
        minion.bind_once("silenced", lambda: player.unbind("spell_cast", increase_attack))
        return minion

class SorcerersApprentice(MinionCard):
    def __init__(self):
        super().__init__("Sorcerer's Apprentice", 2, CHARACTER_CLASS.MAGE, CARD_RARITY.COMMON)

    def create_minion(self, player):
        class Filter:
            def __init__(self):
                self.amount = 1
                self.filter = lambda c: c.is_spell()
                self.min = 0

        filter = Filter()
        minion = Minion(3, 2)
        minion.bind_once("silence", lambda: player.mana_filters.remove(filter))
        minion.bind_once("died", lambda x: player.mana_filters.remove(filter))
        player.mana_filters.append(filter)
        return minion

class KirinTorMage(MinionCard):
    def __init__(self):
        super().__init__("Kirin Tor Mage", 3, CHARACTER_CLASS.MAGE, CARD_RARITY.RARE)

    def create_minion(self, player):
        class Filter:
            def __init__(self):
                #To make sure that no matter what the cost of a secret, it will be 0
                self.amount = 100
                self.filter = lambda c: type(c) in SecretCard.__subclasses__()
                self.min = 0

        def card_used(card):
            if type(card) in SecretCard.__subclasses__():
                player.unbind("card_used", card_used)
                player.unbind("turn_ended", turn_ended)
                player.mana_filters.remove(filter)

        def turn_ended():
            player.unbind("card_used", card_used)
            player.mana_filters.remove(filter)

        filter = Filter()
        minion = Minion(4, 3)
        player.bind("card_used", card_used)
        player.bind_once("turn_ended", turn_ended)
        player.mana_filters.append(filter)

        return minion

class WaterElemental(MinionCard):
    def __init__(self):
        super().__init__("Water Elemental", 4, CHARACTER_CLASS.MAGE, CARD_RARITY.FREE)

    def create_minion(self, player):
        def did_damage(amount, target):
            target.freeze()

        minion = Minion(3, 6)
        minion.bind("did_damage", did_damage)
        return minion<commit_msg>Bug fix for Kirin Tor Mage.<commit_after>import hsgame.cards

__author__ = 'Daniel'

from hsgame.constants import CHARACTER_CLASS, CARD_RARITY, MINION_TYPE
from hsgame.game_objects import MinionCard, Minion, SecretCard

class ManaWyrm(MinionCard):
    def __init__(self):
        super().__init__("Mana Wyrm", 1, CHARACTER_CLASS.MAGE, CARD_RARITY.COMMON)

    def create_minion(self, player):
        def increase_attack(card):
            minion.increase_attack(1)
        minion = Minion(1, 3)
        player.bind("spell_cast", increase_attack)
        minion.bind_once("silenced", lambda: player.unbind("spell_cast", increase_attack))
        return minion

class SorcerersApprentice(MinionCard):
    def __init__(self):
        super().__init__("Sorcerer's Apprentice", 2, CHARACTER_CLASS.MAGE, CARD_RARITY.COMMON)

    def create_minion(self, player):
        class Filter:
            def __init__(self):
                self.amount = 1
                self.filter = lambda c: c.is_spell()
                self.min = 0

        filter = Filter()
        minion = Minion(3, 2)
        minion.bind_once("silence", lambda: player.mana_filters.remove(filter))
        minion.bind_once("died", lambda x: player.mana_filters.remove(filter))
        player.mana_filters.append(filter)
        return minion

class KirinTorMage(MinionCard):
    def __init__(self):
        super().__init__("Kirin Tor Mage", 3, CHARACTER_CLASS.MAGE, CARD_RARITY.RARE)

    def create_minion(self, player):
        def first_secret_cost_zero(m):
            class Filter:
                def __init__(self):
                    #To make sure that no matter what the cost of a secret, it will be 0
                    self.amount = 100
                    self.filter = lambda c: type(c) in SecretCard.__subclasses__()
                    self.min = 0

            def card_used(card):
                if type(card) in SecretCard.__subclasses__():
                    player.unbind("card_used", card_used)
                    player.unbind("turn_ended", turn_ended)
                    player.mana_filters.remove(mana_filter)

            def turn_ended():
                player.unbind("card_used", card_used)
                player.mana_filters.remove(mana_filter)
            mana_filter = Filter()
            player.bind("card_used", card_used)
            player.bind_once("turn_ended", turn_ended)
            player.mana_filters.append(mana_filter)
        minion = Minion(4, 3)
        minion.bind("added_to_board", first_secret_cost_zero)
        return minion

class WaterElemental(MinionCard):
    def __init__(self):
        super().__init__("Water Elemental", 4, CHARACTER_CLASS.MAGE, CARD_RARITY.FREE)

    def create_minion(self, player):
        def did_damage(amount, target):
            target.freeze()

        minion = Minion(3, 6)
        minion.bind("did_damage", did_damage)
        return minion<|endoftext|>


<commit_before># encoding: utf-8

from __future__ import print_function

import os
import platform
from concurrent.futures import ProcessPoolExecutor

import pytest
from click.testing import CliRunner
from simplesqlite import SimpleSQLite
from sqlitebiter._enum import ExitCode
from sqlitebiter.sqlitebiter import cmd

from .common import print_traceback

def fifo_writer(fifo_name):
    with open(fifo_name, "w") as p:
        p.write(
            r"""{"name": "Stacey Chandler", "address": "5514 Daniel Pines Suite 219\nSouth David, WA 31900"}
            {"name": "Joseph Wright", "address": "037 Laura Turnpike\nMartinezfort, MI 92378"}
            {"name": "Mr. Andrew Gomez", "address": "2605 Martin Spur Suite 854\nRowlandhaven, WY 75523"}
            {"name": "Gina Nguyen", "address": "99522 Pamela Land\nNorth Gabriellaport, TX 07851"}
            {"name": "Erika Fisher", "address": "85239 Brandon Underpass Apt. 798\nNorth Destiny, WA 27159"}
            {"name": "Alicia Thomas", "address": "2103 Weaver Drives Apt. 614\nWest Thomasstad, RI 03345"}
            {"name": "John Williams", "address": "1369 Taylor Island Suite 970\nRamirezstad, GA 24877"}
            {"name": "Emily Fitzgerald", "address": "11086 Juan Hill\nLake Marthaburgh, ME 35035"}
            """
        )
        p.flush()

class Test_sqlitebiter_file(object):
    @pytest.mark.skipif("platform.system() == 'Windows'")
    def test_smoke_one_file(self):
        db_path = "test.sqlite"
        runner = CliRunner()

        with runner.isolated_filesystem():
            fifo_name = "jsonl_fifo"

            os.mkfifo(fifo_name)

            with ProcessPoolExecutor() as executor:
                executor.submit(fifo_writer, fifo_name)
                result = runner.invoke(cmd, ["-o", db_path, "file", fifo_name, "--format", "jsonl"])

            print_traceback(result)

            assert result.exit_code == ExitCode.SUCCESS, fifo_name

            assert SimpleSQLite(db_path).fetch_num_records("jsonl_fifo") == 8
<commit_msg>Remove an unused import<commit_after># encoding: utf-8

from __future__ import print_function

import os
from concurrent.futures import ProcessPoolExecutor

import pytest
from click.testing import CliRunner
from simplesqlite import SimpleSQLite
from sqlitebiter._enum import ExitCode
from sqlitebiter.sqlitebiter import cmd

from .common import print_traceback

def fifo_writer(fifo_name):
    with open(fifo_name, "w") as p:
        p.write(
            r"""{"name": "Stacey Chandler", "address": "5514 Daniel Pines Suite 219\nSouth David, WA 31900"}
            {"name": "Joseph Wright", "address": "037 Laura Turnpike\nMartinezfort, MI 92378"}
            {"name": "Mr. Andrew Gomez", "address": "2605 Martin Spur Suite 854\nRowlandhaven, WY 75523"}
            {"name": "Gina Nguyen", "address": "99522 Pamela Land\nNorth Gabriellaport, TX 07851"}
            {"name": "Erika Fisher", "address": "85239 Brandon Underpass Apt. 798\nNorth Destiny, WA 27159"}
            {"name": "Alicia Thomas", "address": "2103 Weaver Drives Apt. 614\nWest Thomasstad, RI 03345"}
            {"name": "John Williams", "address": "1369 Taylor Island Suite 970\nRamirezstad, GA 24877"}
            {"name": "Emily Fitzgerald", "address": "11086 Juan Hill\nLake Marthaburgh, ME 35035"}
            """
        )
        p.flush()

class Test_sqlitebiter_file(object):
    @pytest.mark.skipif("platform.system() == 'Windows'")
    def test_smoke_one_file(self):
        db_path = "test.sqlite"
        runner = CliRunner()

        with runner.isolated_filesystem():
            fifo_name = "jsonl_fifo"

            os.mkfifo(fifo_name)

            with ProcessPoolExecutor() as executor:
                executor.submit(fifo_writer, fifo_name)
                result = runner.invoke(cmd, ["-o", db_path, "file", fifo_name, "--format", "jsonl"])

            print_traceback(result)

            assert result.exit_code == ExitCode.SUCCESS, fifo_name

            assert SimpleSQLite(db_path).fetch_num_records("jsonl_fifo") == 8
<|endoftext|>


<commit_before>import logging

from numpy import array, zeros, ones, concatenate, sin, cos, arcsin

ERROR_TEXT = {
    'VERTICES_SIZE': "Size of vertices array should be a multiple of three, "
                     "but {} provided",
    'TRIANGLES_SHAPE': "Need array of triangles (x, 3), "
                       "but array with shape {} provided",
    'TRIANGLES_VERTICES': "Each triangle should contain 3 vertices, "
                          "but {} provided",
    '3D_VECTOR': "{} should be represented by 3D vector, "
                 "but array of shape {} provided"
}

def process_vector3d(value, name):
    value = array(value)
    if value.shape != (3,):
        raise ValueError(ERROR_TEXT['3D_VECTOR'].format(name, value.shape))
    return value

def spherical_to_cartesian(phi, theta, radius=1.0):
    """Convert spherical coordinates to cartesian.

    Given sinus of polar and azimuthal angle, and radial distance,
    calculate cartesian coordinates of the point.
    """
    sin_phi = sin(phi)
    sin_theta = sin(theta)

    cos_phi = cos(phi)
    cos_theta = cos(theta)

    return array([
        radius * sin_theta * cos_phi,
        radius * sin_theta * sin_phi,
        radius * cos_theta
    ])

class Face:
    """Class to represent Face instances."""

    LIGHT_COMPONENTS_COUNT = 3
    DIRECTION_COMPONENTS_COUNT = 3
    SCALE_COMPONENTS_COUNT = 3
    NON_PCS_COUNT = (
        LIGHT_COMPONENTS_COUNT + DIRECTION_COMPONENTS_COUNT
        + SCALE_COMPONENTS_COUNT)

    DIRECTION_COMPONENTS_START = - NON_PCS_COUNT
    DIRECTION_COMPONENTS_END = (DIRECTION_COMPONENTS_START
                                + DIRECTION_COMPONENTS_COUNT)
    SCALE_COMPONENTS_START = DIRECTION_COMPONENTS_END
    SCALE_COMPONENTS_END = SCALE_COMPONENTS_START + SCALE_COMPONENTS_COUNT
    LIGHT_COMPONENTS_START = SCALE_COMPONENTS_END
    LIGHT_COMPONENTS_END = LIGHT_COMPONENTS_START + LIGHT_COMPONENTS_COUNT

    NON_PCS_SLICE = slice(0, - NON_PCS_COUNT)
    LIGHT_COMPONENTS_SLICE = slice(LIGHT_COMPONENTS_START,
                                   LIGHT_COMPONENTS_END or None)
    DIRECTION_COMPONENTS_SLICE = slice(DIRECTION_COMPONENTS_START,
                                       DIRECTION_COMPONENTS_END or None)
    SCALE_COMPONENTS_SLICE = slice(SCALE_COMPONENTS_START,
                                   SCALE_COMPONENTS_END or None)

    __initial_phi = 0.0
    __initial_theta = 0.0

    def __init__(self, ambient_light=0, directed_light=None,
                 position=None, scale=None, coefficients=None):
        """Create new Face."""
        self.__directed_light = None
        self.__ambient_light = None
        self.__position = None
        self.__scale = None

        self.ambient_light = ambient_light

        if directed_light is None:
            directed_light = zeros(3, dtype='f')
        self.__directed_light = process_vector3d(directed_light, 'Light')

        if position is None:
            position = zeros(3, dtype='f')
        self.__position = process_vector3d(position, 'Position')

        if scale is None:
            scale = ones(3, dtype='f')
        self.__scale = process_vector3d(scale, 'Scale')

        if coefficients is None:
            self.__coefficients = array([], dtype='f')
        else:
            self.__coefficients = array(coefficients, dtype='f')

    @property
    def position(self):
        """Get position."""
        return self.__position

    @property
    def position_cartesian(self):
        """Get directed light vector from spherical coordinates."""
        phi = arcsin(self.__position[0]) + Face.__initial_phi
        theta = arcsin(self.__position[1]) + Face.__initial_theta
        return spherical_to_cartesian(phi, theta)

    @position.setter
    def position(self, position):
        """Set position vector."""
        self.__position = process_vector3d(position, 'Position')

    @property
    def scale(self):
        """Get scale."""
        return self.__scale

    @scale.setter
    def scale(self, scale):
        """Set scales for axes."""
        self.__scale = process_vector3d(scale, 'Shape')

    @property
    def directed_light(self):
        """Get directed light vector."""
        return self.__directed_light

    @property
    def directed_light_cartesian(self):
        """Get directed light vector from spherical coordinates."""
        phi = arcsin(self.__directed_light[0]) + Face.__initial_phi
        theta = arcsin(self.__directed_light[1]) + Face.__initial_theta
        return spherical_to_cartesian(phi, theta)

    @directed_light.setter
    def directed_light(self, directed_light):
        """Set directed light vector."""
        self.__directed_light = process_vector3d(directed_light, 'Light')

    @property
    def ambient_light(self):
        """Get ambient light."""
        return self.__ambient_light

    @ambient_light.setter
    def ambient_light(self, ambient_light):
        """Set ambient light."""
        self.__ambient_light = ambient_light

    @property
    def light(self):
        """Get light vector."""
        return concatenate((self.directed_light, [self.ambient_light]))

    @property
    def light_cartesian(self):
        """Get light from spherical coordinates."""
        return concatenate((self.directed_light_cartesian,
                            [self.directed_light[2]]))

    @property
    def coefficients(self):
        """Get array of Face coefficients."""
        return self.__coefficients

    @property
    def as_array(self):
        """Get NumPy array representation of the Face."""
        result = zeros(self.coefficients.size + Face.NON_PCS_COUNT, dtype='f')
        result[Face.NON_PCS_SLICE] = self.coefficients
        result[Face.DIRECTION_COMPONENTS_SLICE] = self.position
        result[Face.LIGHT_COMPONENTS_SLICE] = self.directed_light
        result[Face.SCALE_COMPONENTS_SLICE] = self.scale
        return result

    @staticmethod
    def set_initial_rotation(phi=0.0, theta=0.0):
        """Set initial rotation of Face.

        Given angles will be added to light and rotation vector.
        """
        Face.__initial_phi = phi
        Face.__initial_theta = theta

    @staticmethod
    def from_array(parameters):
        """Create Face from array of parameters."""
        coefficients = parameters[Face.NON_PCS_SLICE]
        position = parameters[Face.DIRECTION_COMPONENTS_SLICE]
        directed_light = parameters[Face.LIGHT_COMPONENTS_SLICE]
        scale = parameters[Face.SCALE_COMPONENTS_SLICE]

        float_format = '{:>6.04}'
        vector_3d_format = ', '.join([float_format] * 3)
        coefficients_format = ', '.join([float_format] * len(coefficients))

        format_str = ''
        format_str += 'Light: <' + vector_3d_format + '>;'
        format_str += ' Direction: <' + vector_3d_format + '>;'
        format_str += ' Scale: <' + vector_3d_format + '>;'
        format_str += ' Coefficients: <' + coefficients_format + '>'

        components = (directed_light.tolist() + position.tolist()
                      + scale.tolist() + coefficients.tolist())
        logging.debug(format_str.format(*components))

        return Face(coefficients=coefficients,
                    directed_light=directed_light,
                    scale=scale,
                    position=position)
<commit_msg>Removed redundant error messages from Face<commit_after>import logging

from numpy import array, zeros, ones, concatenate, sin, cos, arcsin

ERROR_TEXT = {
    '3D_VECTOR': "{} should be represented by 3D vector, "
                 "but array of shape {} provided"
}

def process_vector3d(value, name):
    value = array(value)
    if value.shape != (3,):
        raise ValueError(ERROR_TEXT['3D_VECTOR'].format(name, value.shape))
    return value

def spherical_to_cartesian(phi, theta, radius=1.0):
    """Convert spherical coordinates to cartesian.

    Given sinus of polar and azimuthal angle, and radial distance,
    calculate cartesian coordinates of the point.
    """
    sin_phi = sin(phi)
    sin_theta = sin(theta)

    cos_phi = cos(phi)
    cos_theta = cos(theta)

    return array([
        radius * sin_theta * cos_phi,
        radius * sin_theta * sin_phi,
        radius * cos_theta
    ])

class Face:
    """Class to represent Face instances."""

    LIGHT_COMPONENTS_COUNT = 3
    DIRECTION_COMPONENTS_COUNT = 3
    SCALE_COMPONENTS_COUNT = 3
    NON_PCS_COUNT = (
        LIGHT_COMPONENTS_COUNT + DIRECTION_COMPONENTS_COUNT
        + SCALE_COMPONENTS_COUNT)

    DIRECTION_COMPONENTS_START = - NON_PCS_COUNT
    DIRECTION_COMPONENTS_END = (DIRECTION_COMPONENTS_START
                                + DIRECTION_COMPONENTS_COUNT)
    SCALE_COMPONENTS_START = DIRECTION_COMPONENTS_END
    SCALE_COMPONENTS_END = SCALE_COMPONENTS_START + SCALE_COMPONENTS_COUNT
    LIGHT_COMPONENTS_START = SCALE_COMPONENTS_END
    LIGHT_COMPONENTS_END = LIGHT_COMPONENTS_START + LIGHT_COMPONENTS_COUNT

    NON_PCS_SLICE = slice(0, - NON_PCS_COUNT)
    LIGHT_COMPONENTS_SLICE = slice(LIGHT_COMPONENTS_START,
                                   LIGHT_COMPONENTS_END or None)
    DIRECTION_COMPONENTS_SLICE = slice(DIRECTION_COMPONENTS_START,
                                       DIRECTION_COMPONENTS_END or None)
    SCALE_COMPONENTS_SLICE = slice(SCALE_COMPONENTS_START,
                                   SCALE_COMPONENTS_END or None)

    __initial_phi = 0.0
    __initial_theta = 0.0

    def __init__(self, ambient_light=0, directed_light=None,
                 position=None, scale=None, coefficients=None):
        """Create new Face."""
        self.__directed_light = None
        self.__ambient_light = None
        self.__position = None
        self.__scale = None

        self.ambient_light = ambient_light

        if directed_light is None:
            directed_light = zeros(3, dtype='f')
        self.__directed_light = process_vector3d(directed_light, 'Light')

        if position is None:
            position = zeros(3, dtype='f')
        self.__position = process_vector3d(position, 'Position')

        if scale is None:
            scale = ones(3, dtype='f')
        self.__scale = process_vector3d(scale, 'Scale')

        if coefficients is None:
            self.__coefficients = array([], dtype='f')
        else:
            self.__coefficients = array(coefficients, dtype='f')

    @property
    def position(self):
        """Get position."""
        return self.__position

    @property
    def position_cartesian(self):
        """Get directed light vector from spherical coordinates."""
        phi = arcsin(self.__position[0]) + Face.__initial_phi
        theta = arcsin(self.__position[1]) + Face.__initial_theta
        return spherical_to_cartesian(phi, theta)

    @position.setter
    def position(self, position):
        """Set position vector."""
        self.__position = process_vector3d(position, 'Position')

    @property
    def scale(self):
        """Get scale."""
        return self.__scale

    @scale.setter
    def scale(self, scale):
        """Set scales for axes."""
        self.__scale = process_vector3d(scale, 'Shape')

    @property
    def directed_light(self):
        """Get directed light vector."""
        return self.__directed_light

    @property
    def directed_light_cartesian(self):
        """Get directed light vector from spherical coordinates."""
        phi = arcsin(self.__directed_light[0]) + Face.__initial_phi
        theta = arcsin(self.__directed_light[1]) + Face.__initial_theta
        return spherical_to_cartesian(phi, theta)

    @directed_light.setter
    def directed_light(self, directed_light):
        """Set directed light vector."""
        self.__directed_light = process_vector3d(directed_light, 'Light')

    @property
    def ambient_light(self):
        """Get ambient light."""
        return self.__ambient_light

    @ambient_light.setter
    def ambient_light(self, ambient_light):
        """Set ambient light."""
        self.__ambient_light = ambient_light

    @property
    def light(self):
        """Get light vector."""
        return concatenate((self.directed_light, [self.ambient_light]))

    @property
    def light_cartesian(self):
        """Get light from spherical coordinates."""
        return concatenate((self.directed_light_cartesian,
                            [self.directed_light[2]]))

    @property
    def coefficients(self):
        """Get array of Face coefficients."""
        return self.__coefficients

    @property
    def as_array(self):
        """Get NumPy array representation of the Face."""
        result = zeros(self.coefficients.size + Face.NON_PCS_COUNT, dtype='f')
        result[Face.NON_PCS_SLICE] = self.coefficients
        result[Face.DIRECTION_COMPONENTS_SLICE] = self.position
        result[Face.LIGHT_COMPONENTS_SLICE] = self.directed_light
        result[Face.SCALE_COMPONENTS_SLICE] = self.scale
        return result

    @staticmethod
    def set_initial_rotation(phi=0.0, theta=0.0):
        """Set initial rotation of Face.

        Given angles will be added to light and rotation vector.
        """
        Face.__initial_phi = phi
        Face.__initial_theta = theta

    @staticmethod
    def from_array(parameters):
        """Create Face from array of parameters."""
        coefficients = parameters[Face.NON_PCS_SLICE]
        position = parameters[Face.DIRECTION_COMPONENTS_SLICE]
        directed_light = parameters[Face.LIGHT_COMPONENTS_SLICE]
        scale = parameters[Face.SCALE_COMPONENTS_SLICE]

        float_format = '{:>6.04}'
        vector_3d_format = ', '.join([float_format] * 3)
        coefficients_format = ', '.join([float_format] * len(coefficients))

        format_str = ''
        format_str += 'Light: <' + vector_3d_format + '>;'
        format_str += ' Direction: <' + vector_3d_format + '>;'
        format_str += ' Scale: <' + vector_3d_format + '>;'
        format_str += ' Coefficients: <' + coefficients_format + '>'

        components = (directed_light.tolist() + position.tolist()
                      + scale.tolist() + coefficients.tolist())
        logging.debug(format_str.format(*components))

        return Face(coefficients=coefficients,
                    directed_light=directed_light,
                    scale=scale,
                    position=position)
<|endoftext|>


<commit_before>"""
Copyright [2009-2017] EMBL-European Bioinformatics Institute
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
     http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import re
import sys

import psycopg2

from portal.management.commands.ftp_exporters.ftp_base import FtpBase
from portal.models import Rna

class FastaExporter(FtpBase):
    """
    Export RNAcentral sequences in FASTA format.

    Manually create Django model instances from raw database results
    in order to reuse the fasta formatting routine defined on the Rna model.
    """
    def __init__(self, *args, **kwargs):
        """
        """
        super(FastaExporter, self).__init__(*args, **kwargs)

        self.subdirectory = self.make_subdirectory(self.destination, self.subfolders['sequences'])
        self.names = {
            'readme': 'readme.txt',
            'seq_active': 'rnacentral_active.fasta',
            'seq_inactive': 'rnacentral_inactive.fasta',
            'nhmmer_db': 'rnacentral_nhmmer.fasta',
            'nhmmer_db_excluded': 'rnacentral_nhmmer_excluded.fasta',
            'species_specific': 'rnacentral_species_specific_ids.fasta',
            'seq_example': 'example.txt',
        }
        self.cursor = None

    def export(self):
        """
        Main export function.
        """
        self.setup()
        self.create_readme()
        self.export_active_sequences()
        self.export_inactive_sequences()
        self.clean_up()
        self.logger.info('Fasta export complete')

    def setup(self):
        """
        Initialize database connection and filehandles.
        """
        self.logger.info('Exporting fasta to %s', self.subdirectory)
        self.get_filenames_and_filehandles(self.names, self.subdirectory)
        self.cursor = self.get_cursor()

    def export_active_sequences(self):
        """
        Export RNA sequences with active cross-references.
        """
        def get_active_sequences_sql():
            """
            Get sequences with at least one active cross-reference.
            """
            if self.test:
                return """SELECT * FROM rna WHERE id < 100"""
            return """
            SELECT t1.upi, t1.seq_short, t1.seq_long
            FROM rna t1, xref t2
            WHERE t1.upi=t2.upi AND t2.deleted='N'
            ORDER BY t1.upi
            """

        def process_active_sequences():
            """
            Create the active.fasta file and the example.fasta file.
            """
            counter = 0
            previous_upi = ''
            valid_chars = re.compile('^[ABCDGHKMNRSTVWXYU]+$', re.IGNORECASE) # IUPAC

            for result in self.cursor:
                if self.test and counter >= self.test_entries:
                    return
                if result['upi'] == previous_upi:
                    continue
                else:
                    previous_upi = result['upi']
                rna = Rna(upi=result['upi'],
                          seq_short=result['seq_short'],
                          seq_long=result['seq_long'])
                fasta = rna.get_sequence_fasta()
                self.filehandles['seq_active'].write(fasta)
                if counter < self.examples:
                    self.filehandles['seq_example'].write(fasta)
                if valid_chars.match(rna.get_sequence()):
                    self.filehandles['nhmmer_db'].write(fasta)
                else:
                    self.filehandles['nhmmer_db_excluded'].write(fasta)
                # species specific identifiers
                sequence = re.sub(r'^>.+?\n', '', fasta) # delete first line
                template = ">{upi}_{taxid} {description}\n{sequence}"
                queryset = rna.xrefs.filter(deleted='N')
                for taxid in set(queryset.values_list('taxid', flat=True)):
                    description = rna.get_description(taxid=taxid)
                    species_specific_fasta = template.format(upi=result['upi'],
                                                             taxid=taxid,
                                                             sequence=sequence,
                                                             description=description)
                    self.filehandles['species_specific'].write(species_specific_fasta)
                counter += 1

        sql = get_active_sequences_sql()
        try:
            self.cursor.execute(sql)
            process_active_sequences()
        except psycopg2.Error, exc:
            self.log_database_error(exc)
            sys.exit(1)

    def export_inactive_sequences(self):
        """
        Export RNA sequences without active cross-references.
        """
        def get_inactive_sequences_sql():
            """
            Get sequences with no active cross-references.
            """
            if self.test:
                return """SELECT * FROM rna WHERE id < 100"""
            return """
            SELECT t1.upi, t1.seq_short, t1.seq_long
            FROM rna t1, xref t2
            WHERE t1.upi = t2.upi AND t2.deleted = 'Y' AND t2.upi NOT IN
                (SELECT upi FROM xref WHERE deleted='N')
            ORDER BY t1.upi
            """

        def process_inactive_sequences():
            """
            Create inactive.fasta file.
            """
            counter = 0
            previous_upi = ''

            for result in self.cursor:
                if self.test and counter > self.test_entries:
                    return
                if result['upi'] == previous_upi:
                    continue
                else:
                    previous_upi = result['upi']
                rna = Rna(upi=result['upi'],
                          seq_short=result['seq_short'],
                          seq_long=result['seq_long'])
                fasta = rna.get_sequence_fasta()
                self.filehandles['seq_inactive'].write(fasta)
                counter += 1

        sql = get_inactive_sequences_sql()
        try:
            self.cursor.execute(sql)
            process_inactive_sequences()
        except psycopg2.Error, exc:
            self.log_database_error(exc)
            sys.exit(1)

    def create_readme(self):
        """
        ===================================================================
        RNAcentral Sequence Data
        ===================================================================

        This directory contains sequences with RNAcentral ids in FASTA format.

        * rnacentral_active.fasta.gz
        Current set of sequences that are present in at least one expert database.

        * rnacentral_inactive.fasta.gz
        All RNAcentral sequences that used to be present in one or more expert database
        but don't have any current cross-references.

        * example.txt
        A small example file showing the format of rnacentral_active.fasta.gz
        and rnacentral_inactive.fasta.gz.
        """
        text = self.create_readme.__doc__
        text = self.format_docstring(text)
        self.filehandles['readme'].write(text)
<commit_msg>Update test SQL query<commit_after>"""
Copyright [2009-2017] EMBL-European Bioinformatics Institute
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
     http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import re
import sys

import psycopg2

from portal.management.commands.ftp_exporters.ftp_base import FtpBase
from portal.models import Rna

class FastaExporter(FtpBase):
    """
    Export RNAcentral sequences in FASTA format.

    Manually create Django model instances from raw database results
    in order to reuse the fasta formatting routine defined on the Rna model.
    """
    def __init__(self, *args, **kwargs):
        """
        """
        super(FastaExporter, self).__init__(*args, **kwargs)

        self.subdirectory = self.make_subdirectory(self.destination, self.subfolders['sequences'])
        self.names = {
            'readme': 'readme.txt',
            'seq_active': 'rnacentral_active.fasta',
            'seq_inactive': 'rnacentral_inactive.fasta',
            'nhmmer_db': 'rnacentral_nhmmer.fasta',
            'nhmmer_db_excluded': 'rnacentral_nhmmer_excluded.fasta',
            'species_specific': 'rnacentral_species_specific_ids.fasta',
            'seq_example': 'example.txt',
        }
        self.cursor = None

    def export(self):
        """
        Main export function.
        """
        self.setup()
        self.create_readme()
        self.export_active_sequences()
        self.export_inactive_sequences()
        self.clean_up()
        self.logger.info('Fasta export complete')

    def setup(self):
        """
        Initialize database connection and filehandles.
        """
        self.logger.info('Exporting fasta to %s', self.subdirectory)
        self.get_filenames_and_filehandles(self.names, self.subdirectory)
        self.cursor = self.get_cursor()

    def export_active_sequences(self):
        """
        Export RNA sequences with active cross-references.
        """
        def get_active_sequences_sql():
            """
            Get sequences with at least one active cross-reference.
            """
            if self.test:
                return """SELECT * FROM rna WHERE id < %i""" % self.test_entries
            return """
            SELECT t1.upi, t1.seq_short, t1.seq_long
            FROM rna t1, xref t2
            WHERE t1.upi=t2.upi AND t2.deleted='N'
            ORDER BY t1.upi
            """

        def process_active_sequences():
            """
            Create the active.fasta file and the example.fasta file.
            """
            counter = 0
            previous_upi = ''
            valid_chars = re.compile('^[ABCDGHKMNRSTVWXYU]+$', re.IGNORECASE) # IUPAC

            for result in self.cursor:
                if self.test and counter >= self.test_entries:
                    return
                if result['upi'] == previous_upi:
                    continue
                else:
                    previous_upi = result['upi']
                rna = Rna(upi=result['upi'],
                          seq_short=result['seq_short'],
                          seq_long=result['seq_long'])
                fasta = rna.get_sequence_fasta()
                self.filehandles['seq_active'].write(fasta)
                if counter < self.examples:
                    self.filehandles['seq_example'].write(fasta)
                if valid_chars.match(rna.get_sequence()):
                    self.filehandles['nhmmer_db'].write(fasta)
                else:
                    self.filehandles['nhmmer_db_excluded'].write(fasta)
                # species specific identifiers
                sequence = re.sub(r'^>.+?\n', '', fasta) # delete first line
                template = ">{upi}_{taxid} {description}\n{sequence}"
                queryset = rna.xrefs.filter(deleted='N')
                for taxid in set(queryset.values_list('taxid', flat=True)):
                    description = rna.get_description(taxid=taxid)
                    species_specific_fasta = template.format(upi=result['upi'],
                                                             taxid=taxid,
                                                             sequence=sequence,
                                                             description=description)
                    self.filehandles['species_specific'].write(species_specific_fasta)
                counter += 1

        sql = get_active_sequences_sql()
        try:
            self.cursor.execute(sql)
            process_active_sequences()
        except psycopg2.Error, exc:
            self.log_database_error(exc)
            sys.exit(1)

    def export_inactive_sequences(self):
        """
        Export RNA sequences without active cross-references.
        """
        def get_inactive_sequences_sql():
            """
            Get sequences with no active cross-references.
            """
            if self.test:
                return """SELECT * FROM rna WHERE id < %i""" % self.test_entries
            return """
            SELECT t1.upi, t1.seq_short, t1.seq_long
            FROM rna t1, xref t2
            WHERE t1.upi = t2.upi AND t2.deleted = 'Y' AND t2.upi NOT IN
                (SELECT upi FROM xref WHERE deleted='N')
            ORDER BY t1.upi
            """

        def process_inactive_sequences():
            """
            Create inactive.fasta file.
            """
            counter = 0
            previous_upi = ''

            for result in self.cursor:
                if self.test and counter > self.test_entries:
                    return
                if result['upi'] == previous_upi:
                    continue
                else:
                    previous_upi = result['upi']
                rna = Rna(upi=result['upi'],
                          seq_short=result['seq_short'],
                          seq_long=result['seq_long'])
                fasta = rna.get_sequence_fasta()
                self.filehandles['seq_inactive'].write(fasta)
                counter += 1

        sql = get_inactive_sequences_sql()
        try:
            self.cursor.execute(sql)
            process_inactive_sequences()
        except psycopg2.Error, exc:
            self.log_database_error(exc)
            sys.exit(1)

    def create_readme(self):
        """
        ===================================================================
        RNAcentral Sequence Data
        ===================================================================

        This directory contains sequences with RNAcentral ids in FASTA format.

        * rnacentral_active.fasta.gz
        Current set of sequences that are present in at least one expert database.

        * rnacentral_inactive.fasta.gz
        All RNAcentral sequences that used to be present in one or more expert database
        but don't have any current cross-references.

        * example.txt
        A small example file showing the format of rnacentral_active.fasta.gz
        and rnacentral_inactive.fasta.gz.
        """
        text = self.create_readme.__doc__
        text = self.format_docstring(text)
        self.filehandles['readme'].write(text)
<|endoftext|>


<commit_before>import ctypes

from numpy import array, cross, dot, zeros_like, mean
from numpy import apply_along_axis, column_stack
from numpy.linalg import norm
import numpy

c_cross = ctypes.cdll.LoadLibrary('./lib_cross.so')  # pylint: disable=C0103

ERROR_TEXT = {
    'VERTICES_SIZE': "Size of vertices array should be a multiple of three, "
                     "but {} provided",
    'TRIANGLES_SHAPE': "Need array of triangles (x, 3), "
                       "but array with shape {} provided",
    'TRIANGLES_VERTICES': "Each triangle should contain 3 vertices, "
                          "but {} provided",
    'LIGHT_DIRECTION': "Light should be represented by 3D vector, "
                       "but array of shape {} provided"
}

def normalize(vertices):
    vertices = vertices.reshape(vertices.size // 3, 3)
    vertices = vertices - vertices.min()
    vertices /= vertices.max()
    vertices -= apply_along_axis(mean, 0, vertices)
    return vertices

class Face:

    __triangles = None
    __triangles_c = None

    def __init__(self, vertices, lights=None):
        if vertices.size % 3 != 0:
            raise ValueError(ERROR_TEXT['VERTICES_SIZE'].format(vertices.size))
        self.__vertices = normalize(vertices)
        self.__vertices_c = self.__vertices.ctypes.get_as_parameter()
        self.__points = None
        self.__light_map = None
        if lights is not None:
            self.set_light(lights)
        else:
            self.__lights = None
        self.__normals = None
        self.__normal_map = None

    def get_vertices(self):
        return self.__vertices

    def get_vertices_c(self):
        return self.__vertices_c

    @staticmethod
    def get_triangles_c():
        return Face.__triangles_c

    @staticmethod
    def get_triangles():
        return Face.__triangles

    def get_light_map(self):
        if self.__light_map is not None:
            return self.__light_map

        self.__light_map = dot(self.get_normals(), self.__light)
        self.__light_map -= self.__light_map.min()
        self.__light_map /= self.__light_map.max()
        self.__light_map /= self.__light_map.max()
        self.__light_map = column_stack([self.__light_map.astype('f')]*3)

        return self.__light_map

    def get_light_map_c(self):
        return self.get_light_map().ctypes.get_as_parameter()

    def get_normal_map_c(self):
        return self.get_normal_map().ctypes.get_as_parameter()

    def get_normals(self):
        if self.__normals is not None:
            return self.__normals

        if self.__points is None:
            assert self.__triangles is not None
            self.__points = self.__vertices[self.__triangles]

        first_edges = self.__points[:, 1] - self.__points[:, 0]
        second_edges = self.__points[:, 2] - self.__points[:, 0]

        normal_vectors = cross(first_edges, second_edges).astype('f')
        normal_vectors_c = normal_vectors.ctypes.get_as_parameter()

        self.__normals = zeros_like(self.__vertices)
        normals_c = self.__normals.ctypes.get_as_parameter()

        c_cross.normals(normal_vectors_c, self.__triangles_c, normals_c,
                        len(self.__triangles))
        c_cross.normalize(normals_c, len(self.__normals))
        return self.__normals

    def get_normal_map(self):
        if self.__normal_map is not None:
            return self.__normal_map

        self.__normal_map = self.get_normals().copy()
        self.__normal_map -= apply_along_axis(numpy.min, 0, self.__normal_map)
        self.__normal_map /= apply_along_axis(numpy.max, 0, self.__normal_map)
        self.__normal_map = self.__normal_map.astype('f')

        return self.__normal_map

    def set_light(self, light):
        light = array(light)
        if light.shape != (3,):
            raise ValueError(ERROR_TEXT['LIGHT_DIRECTION'].format(light.shape))
        self.__light = light / norm(light)
        self.__light_map = None

    @staticmethod
    def set_triangles(triangles=None, triangles_c=None):
        if triangles is not None:
            if len(triangles.shape) != 2:
                raise ValueError(ERROR_TEXT['TRIANGLES_SHAPE']
                                 .format(triangles.shape))
            elif triangles.shape[1] != 3:
                raise ValueError(ERROR_TEXT['TRIANGLES_VERTICES']
                                 .format(triangles.shape[1]))
            Face.__triangles = triangles
        if triangles_c is not None:
            Face.__triangles_c = triangles_c
<commit_msg>Using C function for cross product in Face class<commit_after>import ctypes

from numpy import array, cross, dot, zeros_like, mean, zeros
from numpy import apply_along_axis, column_stack
from numpy.linalg import norm
import numpy

c_cross = ctypes.cdll.LoadLibrary('./lib_cross.so')  # pylint: disable=C0103

ERROR_TEXT = {
    'VERTICES_SIZE': "Size of vertices array should be a multiple of three, "
                     "but {} provided",
    'TRIANGLES_SHAPE': "Need array of triangles (x, 3), "
                       "but array with shape {} provided",
    'TRIANGLES_VERTICES': "Each triangle should contain 3 vertices, "
                          "but {} provided",
    'LIGHT_DIRECTION': "Light should be represented by 3D vector, "
                       "but array of shape {} provided"
}

def normalize(vertices):
    vertices = vertices.reshape(vertices.size // 3, 3)
    vertices = vertices - vertices.min()
    vertices /= vertices.max()
    vertices -= apply_along_axis(mean, 0, vertices)
    return vertices

class Face:

    __triangles = None
    __triangles_c = None

    def __init__(self, vertices, lights=None):
        if vertices.size % 3 != 0:
            raise ValueError(ERROR_TEXT['VERTICES_SIZE'].format(vertices.size))
        self.__vertices = normalize(vertices)
        self.__vertices_c = self.__vertices.ctypes.get_as_parameter()
        self.__points = None
        self.__light_map = None
        if lights is not None:
            self.set_light(lights)
        else:
            self.__lights = None
        self.__normals = None
        self.__normal_map = None

    def get_vertices(self):
        return self.__vertices

    def get_vertices_c(self):
        return self.__vertices_c

    @staticmethod
    def get_triangles_c():
        return Face.__triangles_c

    @staticmethod
    def get_triangles():
        return Face.__triangles

    def get_light_map(self):
        if self.__light_map is not None:
            return self.__light_map

        self.__light_map = dot(self.get_normals(), self.__light)
        self.__light_map -= self.__light_map.min()
        self.__light_map /= self.__light_map.max()
        self.__light_map /= self.__light_map.max()
        self.__light_map = column_stack([self.__light_map.astype('f')]*3)

        return self.__light_map

    def get_light_map_c(self):
        return self.get_light_map().ctypes.get_as_parameter()

    def get_normal_map_c(self):
        return self.get_normal_map().ctypes.get_as_parameter()

    def get_normals(self):
        if self.__normals is not None:
            return self.__normals

        normal_vectors = zeros(self.__triangles.shape, dtype='f')
        normal_vectors_c = normal_vectors.ctypes.get_as_parameter()
        c_cross.cross(self.__vertices_c, self.__triangles_c,
                      normal_vectors_c, len(self.__triangles))

        self.__normals = zeros_like(self.__vertices)
        normals_c = self.__normals.ctypes.get_as_parameter()

        c_cross.normals(normal_vectors_c, self.__triangles_c, normals_c,
                        len(self.__triangles))
        c_cross.normalize(normals_c, len(self.__normals))
        return self.__normals

    def get_normal_map(self):
        if self.__normal_map is not None:
            return self.__normal_map

        self.__normal_map = self.get_normals().copy()
        self.__normal_map -= apply_along_axis(numpy.min, 0, self.__normal_map)
        self.__normal_map /= apply_along_axis(numpy.max, 0, self.__normal_map)
        self.__normal_map = self.__normal_map.astype('f')

        return self.__normal_map

    def set_light(self, light):
        light = array(light)
        if light.shape != (3,):
            raise ValueError(ERROR_TEXT['LIGHT_DIRECTION'].format(light.shape))
        self.__light = light / norm(light)
        self.__light_map = None

    @staticmethod
    def set_triangles(triangles=None, triangles_c=None):
        if triangles is not None:
            if len(triangles.shape) != 2:
                raise ValueError(ERROR_TEXT['TRIANGLES_SHAPE']
                                 .format(triangles.shape))
            elif triangles.shape[1] != 3:
                raise ValueError(ERROR_TEXT['TRIANGLES_VERTICES']
                                 .format(triangles.shape[1]))
            Face.__triangles = triangles
        if triangles_c is not None:
            Face.__triangles_c = triangles_c
<|endoftext|>


<commit_before>
from numpy import array, zeros, concatenate, sin, cos

ERROR_TEXT = {
    'VERTICES_SIZE': "Size of vertices array should be a multiple of three, "
                     "but {} provided",
    'TRIANGLES_SHAPE': "Need array of triangles (x, 3), "
                       "but array with shape {} provided",
    'TRIANGLES_VERTICES': "Each triangle should contain 3 vertices, "
                          "but {} provided",
    'LIGHT_DIRECTION': "Light should be represented by 3D vector, "
                       "but array of shape {} provided"
}

class Face:

    LIGHT_COMPONENTS_COUNT = 3
    DIRECTION_COMPONENTS_COUNT = 0 # 3
    NON_PCS_COUNT = LIGHT_COMPONENTS_COUNT + DIRECTION_COMPONENTS_COUNT

    DIRECTION_COMPONENTS_START = - (LIGHT_COMPONENTS_COUNT
        + DIRECTION_COMPONENTS_COUNT)
    DIRECTION_COMPONENTS_END = (DIRECTION_COMPONENTS_START
        + DIRECTION_COMPONENTS_COUNT)
    LIGHT_COMPONENTS_START = DIRECTION_COMPONENTS_END
    LIGHT_COMPONENTS_END = LIGHT_COMPONENTS_START + LIGHT_COMPONENTS_COUNT

    NON_PCS_SLICE = slice(0, - NON_PCS_COUNT)
    LIGHT_COMPONENTS_SLICE = slice(LIGHT_COMPONENTS_START,
                                   LIGHT_COMPONENTS_END or None)
    DIRECTION_COMPONENTS_SLICE = slice(DIRECTION_COMPONENTS_START,
                                       DIRECTION_COMPONENTS_END or None)

    def __init__(self, ambient_light=0, directed_light=None,
                 coefficients=None):
        """Create new Face."""
        self.__directed_light = None
        self.__ambient_light = None

        self.ambient_light = ambient_light

        if directed_light is None:
            self.directed_light = zeros(3, dtype='f')
        else:
            self.directed_light = directed_light

        if coefficients is None:
            self.__coefficients = array([], dtype='f')
        else:
            self.__coefficients = array(coefficients, dtype='f')

    @property
    def directed_light(self):
        """Get directed light vector."""
        return self.__directed_light

    @property
    def directed_light_cartesian(self):
        """Get directed light vector from spherical coordinates."""
        theta = self.__directed_light[0]
        phi = self.__directed_light[1]
        r = self.__directed_light[2]
        return array([
            r * sin(theta) * cos(phi),
            r * sin(theta) * sin(phi),
            r * cos(theta)
        ])

    @directed_light.setter
    def directed_light(self, directed_light):
        """Set directed light vector."""
        directed_light = array(directed_light)
        if directed_light.shape != (3,):
            raise ValueError(ERROR_TEXT['LIGHT_DIRECTION']
                             .format(directed_light.shape))
        self.__directed_light = directed_light

    @property
    def ambient_light(self):
        """Get ambient light."""
        return self.__ambient_light

    @ambient_light.setter
    def ambient_light(self, ambient_light):
        """Set ambient light."""
        self.__ambient_light = ambient_light

    @property
    def light(self):
        """Get light vector."""
        return concatenate((self.directed_light, [self.ambient_light]))

    @property
    def light_cartesian(self):
        """Get light from spherical coordinates."""
        return concatenate((self.directed_light_cartesian,
                            [self.ambient_light]))

    @property
    def coefficients(self):
        """Get array of Face coefficients."""
        return self.__coefficients

    @staticmethod
    def from_array(parameters):
        """Create Face from array of parameters."""
        coefficients = parameters[:-4]
        ambient_light = parameters[-4]
        directed_light = parameters[-3:]
        return Face(coefficients=coefficients,
                    directed_light=directed_light,
                    ambient_light=ambient_light)
<commit_msg>Changed Face light coordinates fetch<commit_after>
from numpy import array, zeros, concatenate, sin, cos

ERROR_TEXT = {
    'VERTICES_SIZE': "Size of vertices array should be a multiple of three, "
                     "but {} provided",
    'TRIANGLES_SHAPE': "Need array of triangles (x, 3), "
                       "but array with shape {} provided",
    'TRIANGLES_VERTICES': "Each triangle should contain 3 vertices, "
                          "but {} provided",
    'LIGHT_DIRECTION': "Light should be represented by 3D vector, "
                       "but array of shape {} provided"
}

class Face:

    LIGHT_COMPONENTS_COUNT = 3
    DIRECTION_COMPONENTS_COUNT = 0 # 3
    NON_PCS_COUNT = LIGHT_COMPONENTS_COUNT + DIRECTION_COMPONENTS_COUNT

    DIRECTION_COMPONENTS_START = - (LIGHT_COMPONENTS_COUNT
        + DIRECTION_COMPONENTS_COUNT)
    DIRECTION_COMPONENTS_END = (DIRECTION_COMPONENTS_START
        + DIRECTION_COMPONENTS_COUNT)
    LIGHT_COMPONENTS_START = DIRECTION_COMPONENTS_END
    LIGHT_COMPONENTS_END = LIGHT_COMPONENTS_START + LIGHT_COMPONENTS_COUNT

    NON_PCS_SLICE = slice(0, - NON_PCS_COUNT)
    LIGHT_COMPONENTS_SLICE = slice(LIGHT_COMPONENTS_START,
                                   LIGHT_COMPONENTS_END or None)
    DIRECTION_COMPONENTS_SLICE = slice(DIRECTION_COMPONENTS_START,
                                       DIRECTION_COMPONENTS_END or None)

    def __init__(self, ambient_light=0, directed_light=None,
                 coefficients=None):
        """Create new Face."""
        self.__directed_light = None
        self.__ambient_light = None

        self.ambient_light = ambient_light

        if directed_light is None:
            self.directed_light = zeros(3, dtype='f')
        else:
            self.directed_light = directed_light

        if coefficients is None:
            self.__coefficients = array([], dtype='f')
        else:
            self.__coefficients = array(coefficients, dtype='f')

    @property
    def directed_light(self):
        """Get directed light vector."""
        return self.__directed_light

    @property
    def directed_light_cartesian(self):
        """Get directed light vector from spherical coordinates."""
        phi = self.__directed_light[0]
        theta = self.__directed_light[1]
        if False:
            sin_theta = sin(theta)
            cos_theta = cos(theta)
            sin_phi = sin(phi)
            cos_phi = cos(phi)
        else:
            sin_theta = theta
            cos_theta = (1 - theta**2)**.5
            sin_phi = phi
            cos_phi = (1 - phi**2)**.5
        r = 1. #self.__directed_light[2]
        return array([
            r * sin_theta * cos_phi,
            r * sin_theta * sin_phi,
            r * cos_theta
        ])

    @directed_light.setter
    def directed_light(self, directed_light):
        """Set directed light vector."""
        directed_light = array(directed_light)
        if directed_light.shape != (3,):
            raise ValueError(ERROR_TEXT['LIGHT_DIRECTION']
                             .format(directed_light.shape))
        self.__directed_light = directed_light

    @property
    def ambient_light(self):
        """Get ambient light."""
        return self.__ambient_light

    @ambient_light.setter
    def ambient_light(self, ambient_light):
        """Set ambient light."""
        self.__ambient_light = ambient_light

    @property
    def light(self):
        """Get light vector."""
        return concatenate((self.directed_light, [self.ambient_light]))

    @property
    def light_cartesian(self):
        """Get light from spherical coordinates."""
        return concatenate((self.directed_light_cartesian,
                            [self.ambient_light]))

    @property
    def coefficients(self):
        """Get array of Face coefficients."""
        return self.__coefficients

    @staticmethod
    def from_array(parameters):
        """Create Face from array of parameters."""
        coefficients = parameters[:-4]
        ambient_light = parameters[-4]
        directed_light = parameters[-3:]
        return Face(coefficients=coefficients,
                    directed_light=directed_light,
                    ambient_light=ambient_light)
<|endoftext|>