summaryrefslogtreecommitdiff
path: root/silx/gui/plot3d/scene/function.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot3d/scene/function.py')
-rw-r--r--silx/gui/plot3d/scene/function.py497
1 files changed, 0 insertions, 497 deletions
diff --git a/silx/gui/plot3d/scene/function.py b/silx/gui/plot3d/scene/function.py
deleted file mode 100644
index 2921d48..0000000
--- a/silx/gui/plot3d/scene/function.py
+++ /dev/null
@@ -1,497 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2015-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides functions to add to shaders."""
-
-from __future__ import absolute_import, division, unicode_literals
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "17/07/2018"
-
-
-import contextlib
-import logging
-import string
-import numpy
-
-from ... import _glutils
-from ..._glutils import gl
-
-from . import event
-from . import utils
-
-
-_logger = logging.getLogger(__name__)
-
-
-class ProgramFunction(object):
- """Class providing a function to add to a GLProgram shaders.
- """
-
- def setupProgram(self, context, program):
- """Sets-up uniforms of a program using this shader function.
-
- :param RenderContext context: The current rendering context
- :param GLProgram program: The program to set-up.
- It MUST be in use and using this function.
- """
- pass
-
-
-class ClippingPlane(ProgramFunction):
- """Description of a clipping plane and rendering.
-
- Convention: Clipping is performed in camera/eye space.
-
- :param point: Local coordinates of a point on the plane.
- :type point: numpy.ndarray-like of 3 float32
- :param normal: Local coordinates of the plane normal.
- :type normal: numpy.ndarray-like of 3 float32
- """
-
- _fragDecl = """
- /* Clipping plane */
- /* as rx + gy + bz + a > 0, clipping all positive */
- uniform vec4 planeEq;
-
- /* Position is in camera/eye coordinates */
-
- bool isClipped(vec4 position) {
- vec4 tmp = planeEq * position;
- float value = tmp.x + tmp.y + tmp.z + planeEq.a;
- return (value < 0.0001);
- }
-
- void clipping(vec4 position) {
- if (isClipped(position)) {
- discard;
- }
- }
- /* End of clipping */
- """
-
- _fragDeclNoop = """
- bool isClipped(vec4 position)
- {
- return false;
- }
-
- void clipping(vec4 position) {}
- """
-
- def __init__(self, point=(0., 0., 0.), normal=(0., 0., 0.)):
- self._plane = utils.Plane(point, normal)
-
- @property
- def plane(self):
- """Plane parameters in camera space."""
- return self._plane
-
- # GL2
-
- @property
- def fragDecl(self):
- return self._fragDecl if self.plane.isPlane else self._fragDeclNoop
-
- @property
- def fragCall(self):
- return "clipping"
-
- def setupProgram(self, context, program):
- """Sets-up uniforms of a program using this shader function.
-
- :param RenderContext context: The current rendering context
- :param GLProgram program: The program to set-up.
- It MUST be in use and using this function.
- """
- if self.plane.isPlane:
- gl.glUniform4f(program.uniforms['planeEq'], *self.plane.parameters)
-
-
-class DirectionalLight(event.Notifier, ProgramFunction):
- """Description of a directional Phong light.
-
- :param direction: The direction of the light or None to disable light
- :type direction: ndarray of 3 floats or None
- :param ambient: RGB ambient light
- :type ambient: ndarray of 3 floats in [0, 1], default: (1., 1., 1.)
- :param diffuse: RGB diffuse light parameter
- :type diffuse: ndarray of 3 floats in [0, 1], default: (0., 0., 0.)
- :param specular: RGB specular light parameter
- :type specular: ndarray of 3 floats in [0, 1], default: (1., 1., 1.)
- :param int shininess: The shininess of the material for specular term,
- default: 0 which disables specular component.
- """
-
- fragmentShaderFunction = """
- /* Lighting */
- struct DLight {
- vec3 lightDir; // Direction of light in object space
- vec3 ambient;
- vec3 diffuse;
- vec3 specular;
- float shininess;
- vec3 viewPos; // Camera position in object space
- };
-
- uniform DLight dLight;
-
- vec4 lighting(vec4 color, vec3 position, vec3 normal)
- {
- normal = normalize(normal);
- // 1-sided
- float nDotL = max(0.0, dot(normal, - dLight.lightDir));
-
- // 2-sided
- //float nDotL = dot(normal, - dLight.lightDir);
- //if (nDotL < 0.) {
- // nDotL = - nDotL;
- // normal = - normal;
- //}
-
- float specFactor = 0.;
- if (dLight.shininess > 0. && nDotL > 0.) {
- vec3 reflection = reflect(dLight.lightDir, normal);
- vec3 viewDir = normalize(dLight.viewPos - position);
- specFactor = max(0.0, dot(reflection, viewDir));
- if (specFactor > 0.) {
- specFactor = pow(specFactor, dLight.shininess);
- }
- }
-
- vec3 enlightedColor = color.rgb * (dLight.ambient +
- dLight.diffuse * nDotL) +
- dLight.specular * specFactor;
-
- return vec4(enlightedColor.rgb, color.a);
- }
- /* End of Lighting */
- """
-
- fragmentShaderFunctionNoop = """
- vec4 lighting(vec4 color, vec3 position, vec3 normal)
- {
- return color;
- }
- """
-
- def __init__(self, direction=None,
- ambient=(1., 1., 1.), diffuse=(0., 0., 0.),
- specular=(1., 1., 1.), shininess=0):
- super(DirectionalLight, self).__init__()
- self._direction = None
- self.direction = direction # Set _direction
- self._isOn = True
- self._ambient = ambient
- self._diffuse = diffuse
- self._specular = specular
- self._shininess = shininess
-
- ambient = event.notifyProperty('_ambient')
- diffuse = event.notifyProperty('_diffuse')
- specular = event.notifyProperty('_specular')
- shininess = event.notifyProperty('_shininess')
-
- @property
- def isOn(self):
- """True if light is on, False otherwise."""
- return self._isOn and self._direction is not None
-
- @isOn.setter
- def isOn(self, isOn):
- self._isOn = bool(isOn)
-
- @contextlib.contextmanager
- def turnOff(self):
- """Context manager to temporary turn off lighting during rendering.
-
- >>> with light.turnOff():
- ... # Do some rendering without lighting
- """
- wason = self._isOn
- self._isOn = False
- yield
- self._isOn = wason
-
- @property
- def direction(self):
- """The direction of the light, or None if light is not on."""
- return self._direction
-
- @direction.setter
- def direction(self, direction):
- if direction is None:
- self._direction = None
- else:
- assert len(direction) == 3
- direction = numpy.array(direction, dtype=numpy.float32, copy=True)
- norm = numpy.linalg.norm(direction)
- assert norm != 0
- self._direction = direction / norm
- self.notify()
-
- # GL2
-
- @property
- def fragmentDef(self):
- """Definition to add to fragment shader"""
- if self.isOn:
- return self.fragmentShaderFunction
- else:
- return self.fragmentShaderFunctionNoop
-
- @property
- def fragmentCall(self):
- """Function name to call in fragment shader"""
- return "lighting"
-
- def setupProgram(self, context, program):
- """Sets-up uniforms of a program using this shader function.
-
- :param RenderContext context: The current rendering context
- :param GLProgram program: The program to set-up.
- It MUST be in use and using this function.
- """
- if self.isOn and self._direction is not None:
- # Transform light direction from camera space to object coords
- lightdir = context.objectToCamera.transformDir(
- self._direction, direct=False)
- lightdir /= numpy.linalg.norm(lightdir)
-
- gl.glUniform3f(program.uniforms['dLight.lightDir'], *lightdir)
-
- # Convert view position to object coords
- viewpos = context.objectToCamera.transformPoint(
- numpy.array((0., 0., 0., 1.), dtype=numpy.float32),
- direct=False,
- perspectiveDivide=True)[:3]
- gl.glUniform3f(program.uniforms['dLight.viewPos'], *viewpos)
-
- gl.glUniform3f(program.uniforms['dLight.ambient'], *self.ambient)
- gl.glUniform3f(program.uniforms['dLight.diffuse'], *self.diffuse)
- gl.glUniform3f(program.uniforms['dLight.specular'], *self.specular)
- gl.glUniform1f(program.uniforms['dLight.shininess'],
- self.shininess)
-
-
-class Colormap(event.Notifier, ProgramFunction):
-
- _declTemplate = string.Template("""
- uniform struct {
- sampler2D texture;
- bool isLog;
- float min;
- float oneOverRange;
- } cmap;
-
- const float oneOverLog10 = 0.43429448190325176;
-
- vec4 colormap(float value) {
- if (cmap.isLog) { /* Log10 mapping */
- if (value > 0.0) {
- value = clamp(cmap.oneOverRange *
- (oneOverLog10 * log(value) - cmap.min),
- 0.0, 1.0);
- } else {
- value = 0.0;
- }
- } else { /* Linear mapping */
- value = clamp(cmap.oneOverRange * (value - cmap.min), 0.0, 1.0);
- }
-
- $discard
-
- vec4 color = texture2D(cmap.texture, vec2(value, 0.5));
- return color;
- }
- """)
-
- _discardCode = """
- if (value == 0.) {
- discard;
- }
- """
-
- call = "colormap"
-
- NORMS = 'linear', 'log'
- """Tuple of supported normalizations."""
-
- _COLORMAP_TEXTURE_UNIT = 1
- """Texture unit to use for storing the colormap"""
-
- def __init__(self, colormap=None, norm='linear', range_=(1., 10.)):
- """Shader function to apply a colormap to a value.
-
- :param colormap: RGB(A) color look-up table (default: gray)
- :param colormap: numpy.ndarray of numpy.uint8 of dimension Nx3 or Nx4
- :param str norm: Normalization to apply: 'linear' (default) or 'log'.
- :param range_: Range of value to map to the colormap.
- :type range_: 2-tuple of float (begin, end).
- """
- super(Colormap, self).__init__()
-
- # Init privates to default
- self._colormap = None
- self._norm = 'linear'
- self._range = 1., 10.
- self._displayValuesBelowMin = True
-
- self._texture = None
- self._update_texture = True
-
- if colormap is None:
- # default colormap
- colormap = numpy.empty((256, 3), dtype=numpy.uint8)
- colormap[:] = numpy.arange(256,
- dtype=numpy.uint8)[:, numpy.newaxis]
-
- # Set to param values through properties to go through asserts
- self.colormap = colormap
- self.norm = norm
- self.range_ = range_
-
- @property
- def decl(self):
- """Source code of the function declaration"""
- return self._declTemplate.substitute(
- discard="" if self.displayValuesBelowMin else self._discardCode)
-
- @property
- def colormap(self):
- """Color look-up table to use."""
- return numpy.array(self._colormap, copy=True)
-
- @colormap.setter
- def colormap(self, colormap):
- colormap = numpy.array(colormap, copy=True)
- assert colormap.ndim == 2
- assert colormap.shape[1] in (3, 4)
- self._colormap = colormap
- self._update_texture = True
- self.notify()
-
- @property
- def norm(self):
- """Normalization to use for colormap mapping.
-
- Either 'linear' (the default) or 'log' for log10 mapping.
- With 'log' normalization, values <= 0. are set to 1. (i.e. log == 0)
- """
- return self._norm
-
- @norm.setter
- def norm(self, norm):
- if norm != self._norm:
- assert norm in self.NORMS
- self._norm = norm
- if norm == 'log':
- self.range_ = self.range_ # To test for positive range_
- self.notify()
-
- @property
- def range_(self):
- """Range of values to map to the colormap.
-
- 2-tuple of floats: (begin, end).
- The begin value is mapped to the origin of the colormap and the
- end value is mapped to the other end of the colormap.
- The colormap is reversed if begin > end.
- """
- return self._range
-
- @range_.setter
- def range_(self, range_):
- assert len(range_) == 2
- range_ = float(range_[0]), float(range_[1])
-
- if self.norm == 'log' and (range_[0] <= 0. or range_[1] <= 0.):
- _logger.warning(
- "Log normalization and negative range: updating range.")
- minPos = numpy.finfo(numpy.float32).tiny
- range_ = max(range_[0], minPos), max(range_[1], minPos)
-
- if range_ != self._range:
- self._range = range_
- self.notify()
-
- @property
- def displayValuesBelowMin(self):
- """True to display values below colormap min, False to discard them.
- """
- return self._displayValuesBelowMin
-
- @displayValuesBelowMin.setter
- def displayValuesBelowMin(self, displayValuesBelowMin):
- displayValuesBelowMin = bool(displayValuesBelowMin)
- if self._displayValuesBelowMin != displayValuesBelowMin:
- self._displayValuesBelowMin = displayValuesBelowMin
- self.notify()
-
- def setupProgram(self, context, program):
- """Sets-up uniforms of a program using this shader function.
-
- :param RenderContext context: The current rendering context
- :param GLProgram program: The program to set-up.
- It MUST be in use and using this function.
- """
- self.prepareGL2(context) # TODO see how to handle
-
- if self._texture is None: # No colormap
- return
-
- self._texture.bind()
-
- gl.glUniform1i(program.uniforms['cmap.texture'],
- self._texture.texUnit)
- gl.glUniform1i(program.uniforms['cmap.isLog'], self._norm == 'log')
-
- min_, max_ = self.range_
- if self._norm == 'log':
- min_, max_ = numpy.log10(min_), numpy.log10(max_)
-
- gl.glUniform1f(program.uniforms['cmap.min'], min_)
- gl.glUniform1f(program.uniforms['cmap.oneOverRange'],
- (1. / (max_ - min_)) if max_ != min_ else 0.)
-
- def prepareGL2(self, context):
- if self._texture is None or self._update_texture:
- if self._texture is not None:
- self._texture.discard()
-
- colormap = numpy.empty(
- (16, self._colormap.shape[0], self._colormap.shape[1]),
- dtype=self._colormap.dtype)
- colormap[:] = self._colormap
-
- format_ = gl.GL_RGBA if colormap.shape[-1] == 4 else gl.GL_RGB
-
- self._texture = _glutils.Texture(
- format_, colormap, format_,
- texUnit=self._COLORMAP_TEXTURE_UNIT,
- minFilter=gl.GL_NEAREST,
- magFilter=gl.GL_NEAREST,
- wrap=gl.GL_CLAMP_TO_EDGE)
- self._update_texture = False