Skip to content

Commit

Permalink
Added power scale transform
Browse files Browse the repository at this point in the history
  • Loading branch information
rougier committed Jan 4, 2015
1 parent de7d52d commit 0b3c2d0
Show file tree
Hide file tree
Showing 9 changed files with 367 additions and 164 deletions.
13 changes: 8 additions & 5 deletions examples/transform-linear-scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ def on_draw(dt):
window.clear()
points.draw()

#@window.event
#def on_mouse_scroll(x,y,dx,dy):
# if dy < 0: transform["ydomain"] = 1.1*transform["ydomain"]
# else: transform["ydomain"] = transform["ydomain"]/1.1
@window.event
def on_mouse_scroll(x,y,dx,dy):

xdomain, ydomain, zdomain = transform["domain"]
if dy < 0: ydomain *= 1.1
else: ydomain /= 1.1
transform["domain"] = xdomain, ydomain, zdomain


transform = Position3D(LinearScale()) + Viewport()
points = PointCollection("agg", transform = transform)
points.append( P = np.random.normal(0,.5,(10000,3)) )

app.run()
36 changes: 36 additions & 0 deletions examples/transform-power-scale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) 2014, Nicolas P. Rougier
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
import numpy as np
from glumpy import app
from glumpy.graphics.collections import PointCollection
from glumpy.transforms import PowerScale, Position3D, Viewport

window = app.Window(1024,1024, color=(1,1,1,1))

@window.event
def on_draw(dt):
window.clear()
points.draw()

@window.event
def on_mouse_scroll(x,y,dx,dy):
if dy < 0:
transform["exponent"] = np.minimum(10.0, 1.1*transform["exponent"])
else:
transform["exponent"] = np.maximum(0.1, transform["exponent"]/1.1)

transform = Position3D(PowerScale()) + Viewport()
transform["exponent"] = 2
transform["domain"] = -10,+10

points = PointCollection("agg", transform = transform)

P = np.random.uniform(-100,100,(10000,3))
P = np.copysign(np.sqrt(abs(P)),P)
points.append(P)

app.run()
9 changes: 3 additions & 6 deletions glumpy/gloo/snippet.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ def generate_call(self, arguments=None):
if self._next:
operand, other = self.next
s = str(other)
print s
return s

# def __getattr__(self, key):
Expand Down Expand Up @@ -473,19 +472,17 @@ def __setitem__(self, key, value):
# Then we look into all attached program
if len(self._programs) > 0:
name = self.lookup(key)

for program in self._programs:
try:
program[name] = value
except IndexError:
pass
else:
found = True
if not found:
raise IndexError



if not found:
error = 'Snippet does not have such key ("%s")' % key
raise IndexError(error)

# values = []
# for program in self._programs:
Expand Down
62 changes: 62 additions & 0 deletions glumpy/library/transforms/power-scale-forward.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// ----------------------------------------------------------------------------
// Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved.
// Distributed under the (new) BSD License.
// ----------------------------------------------------------------------------
/*
Power scales are similar to linear scales, except there's an exponential
transform that is applied to the input domain value before the output range
value is computed. The mapping to the output range value y can be expressed
as a function of the input domain value x: y = mx^k + b, where k is the
exponent value. Power scales also support negative values, in which case the
input value is multiplied by -1, and the resulting output value is also
multiplied by -1.
*/

uniform int power_scale_clamp;
uniform vec3 power_scale_exponent;
uniform vec4 power_scale_x;
uniform vec4 power_scale_y;
uniform vec4 power_scale_z;


float power_scale_forward(float value)
{
float domain_inf = power_scale_x.x;
float domain_sup = power_scale_x.y;
float range_inf = power_scale_x.z;
float range_sup = power_scale_x.w;
float exponent = power_scale_exponent.x;

float v = pow(abs(value), exponent);
float t = (v - domain_inf) /(domain_sup - domain_inf);
if (power_scale_clamp > 0) t = clamp(t,0.0,1.0);
return sign(value) * (range_inf + t*(range_sup - range_inf));
}

vec2 power_scale_forward(vec2 value)
{
vec2 domain_inf = vec2(power_scale_x.x, power_scale_y.x);
vec2 domain_sup = vec2(power_scale_x.y, power_scale_y.y);
vec2 range_inf = vec2(power_scale_x.z, power_scale_y.z);
vec2 range_sup = vec2(power_scale_x.w, power_scale_y.w);
vec2 exponent = power_scale_exponent.xy;

vec2 v = pow(abs(value), exponent);
vec2 t = (v - domain_inf) /(domain_sup - domain_inf);
if (power_scale_clamp > 0) t = clamp(t,0.0,1.0);
return sign(value) * (range_inf + t*(range_sup - range_inf));
}

vec3 power_scale_forward(vec3 value)
{
vec3 domain_inf = vec3(power_scale_x.x, power_scale_y.x, power_scale_z.x);
vec3 domain_sup = vec3(power_scale_x.y, power_scale_y.y, power_scale_z.y);
vec3 range_inf = vec3(power_scale_x.z, power_scale_y.z, power_scale_z.z);
vec3 range_sup = vec3(power_scale_x.w, power_scale_y.w, power_scale_z.w);
vec3 exponent = power_scale_exponent.xyz;

vec3 v = pow(abs(value), exponent);
vec3 t = (v - domain_inf) /(domain_sup - domain_inf);
if (power_scale_clamp > 0) t = clamp(t,0.0,1.0);
return sign(value) * (range_inf + t*(range_sup - range_inf));
}
5 changes: 5 additions & 0 deletions glumpy/library/transforms/power-scale.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// ----------------------------------------------------------------------------
// Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved.
// Distributed under the (new) BSD License.
// ----------------------------------------------------------------------------
#include "transforms/power-scale-forward.glsl"
4 changes: 3 additions & 1 deletion glumpy/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
from translate import Translate
from position2d import Position2D
from position3d import Position3D
from linear_scale import LinearScale
from pvm_projection import PVMProjection
from orthographic_projection import OrthographicProjection

from power_scale import PowerScale
from linear_scale import LinearScale


# from polar import Polar
# from rotate import Rotate
Expand Down
159 changes: 7 additions & 152 deletions glumpy/transforms/linear_scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
The transform is connected to the following events:
* attach (initialization)
* data (update)
Relevant shader code:
Expand All @@ -29,15 +28,16 @@
import numpy as np
from glumpy import library
from . transform import Transform
from . quantitative_scale import QuantitativeScale


class LinearScale(Transform):
class LinearScale(QuantitativeScale):
""" Linear scale transform """

aliases = { "x" : "linear_scale_x",
"y" : "linear_scale_y",
"z" : "linear_scale_z",
"clamp" : "linear_scale_clamp" }
aliases = { "scale_x" : "linear_scale_x",
"scale_y" : "linear_scale_y",
"scale_z" : "linear_scale_z",
"clamp" : "linear_scale_clamp" }


def __init__(self, *args, **kwargs):
Expand All @@ -57,150 +57,5 @@ def __init__(self, *args, **kwargs):
clamp : bool (default is True)
Clamping test for xyz
"""

code = library.get("transforms/linear-scale-forward.glsl")
Transform.__init__(self, code, *args, **kwargs)

self._scales = np.zeros((3,4), dtype=np.float32)
self._scales[0] = -1,+1, -1,+1
self._scales[1] = -1,+1, -1,+1
self._scales[2] = -1,+1, -1,+1
self._clamp = False

self.domain = Transform._get_kwarg("domain", kwargs) or (-1,+1)
self.range = Transform._get_kwarg("range", kwargs) or (-1,+1)
self.clamp = Transform._get_kwarg("clamp", kwargs) or False


@property
def domain(self):
""" Input domain for xyz """
return self._scales[:,:2]

@domain.setter
def domain(self, value):
self._scales[:,:2] = value
if self.is_attached:
self["linear_scale_x"] = self._scales[0]
self["linear_scale_y"] = self._scales[1]
self["linear_scale_z"] = self._scales[2]

@property
def xdomain(self):
""" Input domain for x"""
return self._scales[0,:2]

@xdomain.setter
def xdomain(self, value):
self._scales[0,:2] = value
if self.is_attached:
self["linear_scale_x"] = self._scales[0]

@property
def ydomain(self):
""" Input domain for y"""
return self._scales[1,:2]

@ydomain.setter
def ydomain(self, value):
self._scales[1,:2] = value
if self.is_attached:
self["linear_scale_y"] = self._scales[1]

@property
def zdomain(self):
""" Input domain for z"""
return self._scales[2,:2]

@zdomain.setter
def zdomain(self, value):
self._scales[2,:2] = value
if self.is_attached:
self["linear_scale_z"] = self._scales[2]

@property
def range(self):
""" Output range for xyz"""

return self._scales[:,2:]

@range.setter
def range(self, value):
self._scales[:,2:] = value

if self.is_attached:
self["linear_scale_x"] = self._scales[0]
self["linear_scale_y"] = self._scales[1]
self["linear_scale_z"] = self._scales[2]

@property
def xrange(self):
""" Input range for x"""
return self._scales[0,2:]

@xrange.setter
def xrange(self, value):
self._scales[0,2:] = value
if self.is_attached:
self["linear_scale_x"] = self._scales[0]

@property
def yrange(self):
""" Input range for y"""
return self._scales[1,2:]

@yrange.setter
def yrange(self, value):
self._scales[1,2:] = value
if self.is_attached:
self["linear_scale_y"] = self._scales[1]

@property
def zrange(self):
""" Input range for z"""
return self._scales[2,2:]

@zrange.setter
def zrange(self, value):
self._scales[2,2:] = value
if self.is_attached:
self["linear_scale_z"] = self._scales[2]

@property
def clamp(self):
""" Whether to clamp xyz values """
return self._clamp

@clamp.setter
def clamp(self, value):
self._clamp = value
if self.is_attached:
self["clamp"] = self._clamp


def __getitem__(self, key):
""" Override getitem to enforce aliases """

if key in LinearScale.aliases.keys():
key = LinearScale.aliases[key]
return getattr(self,key)
return Transform.__getitem__(self, key)


def __setitem__(self, key, value):
""" Override getitem to enforce aliases """

if key in LinearScale.aliases.keys():
key = LinearScale.aliases[key]
setattr(self,key,value)
else:
Transform.__setitem__(self, key, value)


def on_attach(self, program):
""" Initialization event """

self["linear_scale_clamp"] = self._clamp
self["linear_scale_x"] = self._scales[0]
self["linear_scale_y"] = self._scales[1]
self["linear_scale_z"] = self._scales[2]
QuantitativeScale.__init__(self, code, *args, **kwargs)
Loading

0 comments on commit 0b3c2d0

Please sign in to comment.