"Fossies" - the Fresh Open Source Software Archive

Member "veusz-3.1/veusz/widgets/volume3d.py" (29 May 2018, 9377 Bytes) of package /linux/privat/veusz-3.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "volume3d.py" see the Fossies "Dox" file reference documentation.

    1 #    Copyright (C) 2017 Jeremy S. Sanders
    2 #    Email: Jeremy Sanders <jeremy@jeremysanders.net>
    3 #
    4 #    This program is free software; you can redistribute it and/or modify
    5 #    it under the terms of the GNU General Public License as published by
    6 #    the Free Software Foundation; either version 2 of the License, or
    7 #    (at your option) any later version.
    8 #
    9 #    This program is distributed in the hope that it will be useful,
   10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12 #    GNU General Public License for more details.
   13 #
   14 #    You should have received a copy of the GNU General Public License along
   15 #    with this program; if not, write to the Free Software Foundation, Inc.,
   16 #    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   17 ###############################################################################
   18 
   19 """3D volume plotting widget."""
   20 
   21 from __future__ import division, print_function
   22 import numpy as N
   23 
   24 from .. import qtall as qt
   25 from .. import setting
   26 from .. import document
   27 from .. import utils
   28 from ..helpers import threed
   29 
   30 from . import plotters3d
   31 
   32 def _(text, disambiguation=None, context='Volume3D'):
   33     """Translate text."""
   34     return qt.QCoreApplication.translate(context, text, disambiguation)
   35 
   36 def autoedges(data, fillfactor):
   37     # make sure boxes do not overlap
   38     fillfactor = min(0.999, fillfactor)
   39 
   40     unique, idxs = N.unique(data, return_inverse=True)
   41     if len(unique) < 2:
   42         return None, None
   43     deltas = unique[1:]-unique[:-1]
   44 
   45     minvals = N.hstack((
   46         [unique[0]-0.5*fillfactor*deltas[0]],
   47         unique[1:]-0.5*fillfactor*deltas))
   48     maxvals = N.hstack((
   49         unique[:-1]+0.5*fillfactor*deltas,
   50         [unique[-1]+0.5*fillfactor*deltas[-1]]))
   51 
   52     edges = N.column_stack((minvals,maxvals))
   53 
   54     return edges, idxs
   55 
   56 class DataColor(setting.DataColor):
   57     def __init__(self, *args, **argsv):
   58         setting.DataColor.__init__(self, *args, **argsv)
   59         self.get('points').newDefault('v')
   60 
   61 class Volume3D(plotters3d.GenericPlotter3D):
   62     """Plotting points in 3D."""
   63 
   64     typename='volume3d'
   65     description=_('3D volume')
   66     allowusercreation=True
   67 
   68     @classmethod
   69     def addSettings(klass, s):
   70         plotters3d.GenericPlotter3D.addSettings(s)
   71 
   72         s.add( setting.DatasetExtended(
   73             'transData', '',
   74             descr=_('Transparency dataset, optional, 0-1'),
   75             usertext=_('Transparency')), 0 )
   76         s.add( setting.DatasetExtended(
   77             'zData', 'z',
   78             descr=_('Z dataset'),
   79             usertext=_('Z data')), 0 )
   80         s.add( setting.DatasetExtended(
   81             'yData', 'y',
   82             descr=_('Y dataset'),
   83             usertext=_('Y data')), 0 )
   84         s.add( setting.DatasetExtended(
   85             'xData', 'x',
   86             descr=_('X dataset'),
   87             usertext=_('X data')), 0 )
   88         s.add(DataColor(
   89             'DataColor', dimensions=1),
   90               0)
   91 
   92         s.add(setting.Colormap(
   93             'colorMap',
   94             'grey',
   95             descr = _('Set of colors to plot data with'),
   96             usertext=_('Colormap'),
   97             formatting=True),
   98             0)
   99         s.add(setting.Bool(
  100             'colorInvert', False,
  101             descr = _('Invert color map'),
  102             usertext=_('Invert colormap'),
  103             formatting=True),
  104             1)
  105         s.add(setting.Int(
  106             'transparency', 50,
  107             descr = _('Transparency percentage'),
  108             usertext = _('Transparency'),
  109             minval = 0,
  110             maxval = 100,
  111             formatting=True),
  112             2)
  113         s.add(setting.Int(
  114             'reflectivity', 0,
  115             minval=0, maxval=100,
  116             descr=_('Reflectivity percentage'),
  117             usertext=_('Reflectivity'),
  118             formatting=True),
  119             3)
  120         s.add(setting.Float(
  121             'fillfactor', 1,
  122             minval=0, maxval=1,
  123             descr=_('Filling factor (0-1)'),
  124             usertext=_('Fill factor'),
  125             formatting=True),
  126             4)
  127 
  128         s.add(setting.Line3D(
  129             'Line',
  130             descr = _('Line settings'),
  131             usertext = _('Box line')),
  132                pixmap = 'settings_plotline' )
  133 
  134     def affectsAxisRange(self):
  135         """Which axes this widget affects."""
  136         s = self.settings
  137         return ((s.xAxis, 'sx'), (s.yAxis, 'sy'), (s.zAxis, 'sz'))
  138 
  139     def getRange(self, axis, depname, axrange):
  140         """Update axis range from data."""
  141 
  142         dataname = {'sx': 'xData', 'sy': 'yData', 'sz': 'zData'}[depname]
  143         dsetn = self.settings.get(dataname)
  144         data = dsetn.getData(self.document)
  145 
  146         if axis.settings.log:
  147             def updateRange(v):
  148                 with N.errstate(invalid='ignore'):
  149                     chopzero = v[(v>0) & N.isfinite(v)]
  150                 if len(chopzero) > 0:
  151                     axrange[0] = min(axrange[0], chopzero.min())
  152                     axrange[1] = max(axrange[1], chopzero.max())
  153         else:
  154             def updateRange(v):
  155                 fvals = v[N.isfinite(v)]
  156                 if len(fvals) > 0:
  157                     axrange[0] = min(axrange[0], fvals.min())
  158                     axrange[1] = max(axrange[1], fvals.max())
  159 
  160         if data:
  161             data.rangeVisit(updateRange)
  162 
  163     def _getdata(self, axes):
  164         s = self.settings
  165         doc = self.document
  166         xv = s.get('xData').getData(doc)
  167         yv = s.get('yData').getData(doc)
  168         zv = s.get('zData').getData(doc)
  169         vv = s.DataColor.get('points').getData(doc)
  170         if not xv or not yv or not zv or not vv:
  171             return
  172         trans = s.get('transData').getData(doc)
  173 
  174         # numpy arrays
  175         xv = xv.data
  176         yv = yv.data
  177         zv = zv.data
  178         vv = vv.data
  179         trans = None if trans is None else N.clip(trans.data, 0, 1)
  180 
  181         # trim to length
  182         minlen = min(len(xv),len(yv),len(zv),len(vv))
  183         if trans is not None:
  184             minlen = min(minlen, len(trans))
  185 
  186         xv = axes[0].transformToAxis(xv[:minlen])
  187         yv = axes[1].transformToAxis(yv[:minlen])
  188         zv = axes[2].transformToAxis(zv[:minlen])
  189         vv = vv[:minlen]
  190         trans = None if trans is None else trans[:minlen]
  191 
  192         # get bits with valid coordinates
  193         valid = N.isfinite(xv) & N.isfinite(yv) & N.isfinite(zv)
  194         if not N.all(valid):
  195             xv = xv[valid]
  196             yv = yv[valid]
  197             zv = zv[valid]
  198             vv = vv[valid]
  199             trans = None if trans is None else trans[valid]
  200 
  201         # get edges of boxes in graph coordinates
  202         ff = self.settings.fillfactor
  203         xedges, xidxs = autoedges(xv, ff)
  204         yedges, yidxs = autoedges(yv, ff)
  205         zedges, zidxs = autoedges(zv, ff)
  206         if xedges is None or yedges is None or zedges is None:
  207             return
  208 
  209         # select finite values
  210         valid = N.isfinite(vv)
  211         if trans is not None:
  212             valid &= N.isfinite(trans)
  213         if not N.all(valid):
  214             xidxs = xidxs[valid]
  215             yidxs = yidxs[valid]
  216             zidxs = zidxs[valid]
  217             vv = vv[valid]
  218             trans = None if trans is None else trans[valid]
  219 
  220         # transform back edges from axis coordinates
  221         xedges = axes[0].transformFromAxis(xedges)
  222         yedges = axes[1].transformFromAxis(yedges)
  223         zedges = axes[2].transformFromAxis(zedges)
  224 
  225         return utils.Struct(
  226             xedges=xedges, xidxs=xidxs, yedges=yedges, yidxs=yidxs,
  227             zedges=zedges, zidxs=zidxs,
  228             vals=vv, trans=trans)
  229 
  230     def dataDrawToObject(self, painter, axes):
  231         """Do drawing of axis."""
  232 
  233         s = self.settings
  234 
  235         axes = self.fetchAxes()
  236         if axes is None:
  237             return
  238 
  239         data = self._getdata(axes)
  240         if data is None:
  241             return
  242 
  243         cmap = self.document.evaluate.getColormap(
  244             s.colorMap, s.colorInvert)
  245         cdata = data.vals.reshape((1, data.vals.size))
  246         if data.trans is not None:
  247             transimg = data.trans.reshape((1, data.vals.size))
  248         else:
  249             transimg = None
  250 
  251         colorimg = utils.applyColorMap(
  252             cmap, s.DataColor.scaling,
  253             cdata,
  254             s.DataColor.min, s.DataColor.max,
  255             s.transparency, transimg=transimg)
  256 
  257         surfprop = threed.SurfaceProp(refl=s.reflectivity*0.01)
  258         surfprop.setRGBs(colorimg)
  259 
  260         lineprop = s.Line.makeLineProp(painter)
  261 
  262         # convert from axis coordinates
  263         xedges = axes[0].dataToLogicalCoords(data.xedges)
  264         yedges = axes[1].dataToLogicalCoords(data.yedges)
  265         zedges = axes[2].dataToLogicalCoords(data.zedges)
  266 
  267         # get minimum and maximum of cubes
  268         xmin = threed.ValVector(xedges[data.xidxs,0])
  269         xmax = threed.ValVector(xedges[data.xidxs,1])
  270         ymin = threed.ValVector(yedges[data.yidxs,0])
  271         ymax = threed.ValVector(yedges[data.yidxs,1])
  272         zmin = threed.ValVector(zedges[data.zidxs,0])
  273         zmax = threed.ValVector(zedges[data.zidxs,1])
  274 
  275         clipcont = self.makeClipContainer(axes)
  276         cuboids = threed.MultiCuboid(
  277             xmin, xmax, ymin, ymax, zmin, zmax, lineprop, surfprop)
  278         clipcont.addObject(cuboids)
  279 
  280         clipcont.assignWidgetId(id(self))
  281         return clipcont
  282 
  283 document.thefactory.register(Volume3D)