Skip to content

Commit e7fcd30

Browse files
committed
Refactor bezierbuilder to avoid eval calls.
This will help with pep8 naming convention compliance without breaking the user-facing API. Specifically, we can lowercase the names of the Bezier and CatmulClark functions without having to lowercase the string argument values to the CLI.
1 parent e9104b3 commit e7fcd30

File tree

2 files changed

+86
-81
lines changed

2 files changed

+86
-81
lines changed

viscm/bezierbuilder.py renamed to viscm/bezierbuilder/__init__.py

Lines changed: 27 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,36 @@
1-
# BézierBuilder
2-
#
3-
# Copyright (c) 2013, Juan Luis Cano Rodríguez <juanlu001@gmail.com>
4-
# All rights reserved.
5-
#
6-
# Redistribution and use in source and binary forms, with or without modification,
7-
# are permitted provided that the following conditions are met:
8-
#
9-
# * Redistributions of source code must retain the above copyright notice,
10-
# this list of conditions and the following disclaimer.
11-
# * Redistributions in binary form must reproduce the above copyright notice,
12-
# this list of conditions and the following disclaimer in the documentation
13-
# and/or other materials provided with the distribution.
14-
#
15-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16-
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17-
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18-
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
19-
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20-
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21-
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22-
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23-
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24-
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25-
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26-
271
"""BézierBuilder, an interactive Bézier curve explorer.
282
29-
Just run it with
30-
31-
$ python bezier_builder.py
32-
3+
Copyright (c) 2013, Juan Luis Cano Rodríguez <juanlu001@gmail.com>
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without modification,
7+
are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice,
10+
this list of conditions and the following disclaimer.
11+
* Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
19+
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3326
"""
3427

35-
from math import factorial
36-
3728
import numpy as np
3829
from matplotlib.backends.qt_compat import QtCore
3930
from matplotlib.lines import Line2D
4031

41-
from .minimvc import Trigger
32+
from viscm.bezierbuilder.curve import curve_method
33+
from viscm.minimvc import Trigger
4234

4335

4436
class ControlPointModel:
@@ -193,7 +185,7 @@ def compute_arc_length(xp, yp, method, t=None, grid=256):
193185

194186
class SingleBezierCurveModel:
195187
def __init__(self, control_point_model, method="CatmulClark"):
196-
self.method = eval(method)
188+
self.method = curve_method[method]
197189
self.control_point_model = control_point_model
198190
x, y = self.get_bezier_points()
199191
self.bezier_curve = Line2D(x, y)
@@ -215,7 +207,7 @@ def _refresh(self):
215207

216208
class TwoBezierCurveModel:
217209
def __init__(self, control_point_model, method="CatmulClark"):
218-
self.method = eval(method)
210+
self.method = curve_method[method]
219211
self.control_point_model = control_point_model
220212
x, y = self.get_bezier_points()
221213
self.bezier_curve = Line2D(x, y)
@@ -286,49 +278,3 @@ def _refresh(self):
286278
x, y = self.bezier_curve_model.get_bezier_points()
287279
self.bezier_curve.set_data(x, y)
288280
self.canvas.draw()
289-
290-
291-
# We used to use scipy.special.binom here,
292-
# but reimplementing it ourself lets us avoid pulling in a dependency
293-
# scipy just for that one function.
294-
def binom(n, k):
295-
return factorial(n) * 1.0 / (factorial(k) * factorial(n - k))
296-
297-
298-
def Bernstein(n, k):
299-
"""Bernstein polynomial."""
300-
coeff = binom(n, k)
301-
302-
def _bpoly(x):
303-
return coeff * x**k * (1 - x) ** (n - k)
304-
305-
return _bpoly
306-
307-
308-
def Bezier(points, at):
309-
"""Build Bézier curve from points.
310-
Deprecated. CatmulClark builds nicer splines
311-
"""
312-
at = np.asarray(at)
313-
at_flat = at.ravel()
314-
N = len(points)
315-
curve = np.zeros((at_flat.shape[0], 2))
316-
for ii in range(N):
317-
curve += np.outer(Bernstein(N - 1, ii)(at_flat), points[ii])
318-
return curve.reshape((*at.shape, 2))
319-
320-
321-
def CatmulClark(points, at):
322-
points = np.asarray(points)
323-
324-
while len(points) < len(at):
325-
new_p = np.zeros((2 * len(points), 2))
326-
new_p[0] = points[0]
327-
new_p[-1] = points[-1]
328-
new_p[1:-2:2] = 3 / 4.0 * points[:-1] + 1 / 4.0 * points[1:]
329-
new_p[2:-1:2] = 1 / 4.0 * points[:-1] + 3 / 4.0 * points[1:]
330-
points = new_p
331-
xp, yp = zip(*points)
332-
xp = np.interp(at, np.linspace(0, 1, len(xp)), xp)
333-
yp = np.interp(at, np.linspace(0, 1, len(yp)), yp)
334-
return np.asarray(list(zip(xp, yp)))

viscm/bezierbuilder/curve.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from math import factorial
2+
3+
import numpy as np
4+
5+
6+
def binom(n, k):
7+
"""Re-implement `scipy.special.binom`.
8+
9+
Reimplementing it ourself lets us avoid pulling in a dependency scipy just for that
10+
one function.
11+
12+
FIXME: `scipy` is already a dependency. Delete this?
13+
"""
14+
return factorial(n) * 1.0 / (factorial(k) * factorial(n - k))
15+
16+
17+
def Bernstein(n, k):
18+
"""Bernstein polynomial."""
19+
coeff = binom(n, k)
20+
21+
def _bpoly(x):
22+
return coeff * x**k * (1 - x) ** (n - k)
23+
24+
return _bpoly
25+
26+
27+
def Bezier(points, at):
28+
"""Build Bézier curve from points.
29+
Deprecated. CatmulClark builds nicer splines
30+
"""
31+
at = np.asarray(at)
32+
at_flat = at.ravel()
33+
N = len(points)
34+
curve = np.zeros((at_flat.shape[0], 2))
35+
for ii in range(N):
36+
curve += np.outer(Bernstein(N - 1, ii)(at_flat), points[ii])
37+
return curve.reshape((*at.shape, 2))
38+
39+
40+
def CatmulClark(points, at):
41+
points = np.asarray(points)
42+
43+
while len(points) < len(at):
44+
new_p = np.zeros((2 * len(points), 2))
45+
new_p[0] = points[0]
46+
new_p[-1] = points[-1]
47+
new_p[1:-2:2] = 3 / 4.0 * points[:-1] + 1 / 4.0 * points[1:]
48+
new_p[2:-1:2] = 1 / 4.0 * points[:-1] + 3 / 4.0 * points[1:]
49+
points = new_p
50+
xp, yp = zip(*points)
51+
xp = np.interp(at, np.linspace(0, 1, len(xp)), xp)
52+
yp = np.interp(at, np.linspace(0, 1, len(yp)), yp)
53+
return np.asarray(list(zip(xp, yp)))
54+
55+
56+
curve_method = {
57+
"Bezier": Bezier,
58+
"CatmulClark": CatmulClark,
59+
}

0 commit comments

Comments
 (0)