code dump 2
This commit is contained in:
parent
f0357aa8ec
commit
0d52eb4a9f
|
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
from numpy import *
|
||||||
|
|
||||||
|
|
||||||
|
# evaluates cubic bezier at t, return point
|
||||||
|
def q(ctrlPoly, t):
|
||||||
|
return (1.0-t)**3 * ctrlPoly[0] + 3*(1.0-t)**2 * t * ctrlPoly[1] + 3*(1.0-t)* t**2 * ctrlPoly[2] + t**3 * ctrlPoly[3]
|
||||||
|
|
||||||
|
|
||||||
|
# evaluates cubic bezier first derivative at t, return point
|
||||||
|
def qprime(ctrlPoly, t):
|
||||||
|
return 3*(1.0-t)**2 * (ctrlPoly[1]-ctrlPoly[0]) + 6*(1.0-t) * t * (ctrlPoly[2]-ctrlPoly[1]) + 3*t**2 * (ctrlPoly[3]-ctrlPoly[2])
|
||||||
|
|
||||||
|
|
||||||
|
# evaluates cubic bezier second derivative at t, return point
|
||||||
|
def qprimeprime(ctrlPoly, t):
|
||||||
|
return 6*(1.0-t) * (ctrlPoly[2]-2*ctrlPoly[1]+ctrlPoly[0]) + 6*(t) * (ctrlPoly[3]-2*ctrlPoly[2]+ctrlPoly[1])
|
||||||
|
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
""" Python implementation of
|
||||||
|
Algorithm for Automatically Fitting Digitized Curves
|
||||||
|
by Philip J. Schneider
|
||||||
|
"Graphics Gems", Academic Press, 1990
|
||||||
|
"""
|
||||||
|
from __future__ import print_function
|
||||||
|
from numpy import *
|
||||||
|
import bezier
|
||||||
|
|
||||||
|
|
||||||
|
# Fit one (ore more) Bezier curves to a set of points
|
||||||
|
def fitCurve(points, maxError):
|
||||||
|
leftTangent = normalize(points[1] - points[0])
|
||||||
|
rightTangent = normalize(points[-2] - points[-1])
|
||||||
|
return fitCubic(points, leftTangent, rightTangent, maxError)
|
||||||
|
|
||||||
|
|
||||||
|
def fitCubic(points, leftTangent, rightTangent, error):
|
||||||
|
# Use heuristic if region only has two points in it
|
||||||
|
if (len(points) == 2):
|
||||||
|
dist = linalg.norm(points[0] - points[1]) / 3.0
|
||||||
|
bezCurve = [points[0], points[0] + leftTangent * dist, points[1] + rightTangent * dist, points[1]]
|
||||||
|
return [bezCurve]
|
||||||
|
|
||||||
|
# Parameterize points, and attempt to fit curve
|
||||||
|
u = chordLengthParameterize(points)
|
||||||
|
bezCurve = generateBezier(points, u, leftTangent, rightTangent)
|
||||||
|
# Find max deviation of points to fitted curve
|
||||||
|
maxError, splitPoint = computeMaxError(points, bezCurve, u)
|
||||||
|
if maxError < error:
|
||||||
|
return [bezCurve]
|
||||||
|
|
||||||
|
# If error not too large, try some reparameterization and iteration
|
||||||
|
if maxError < error**2:
|
||||||
|
for i in range(20):
|
||||||
|
uPrime = reparameterize(bezCurve, points, u)
|
||||||
|
bezCurve = generateBezier(points, uPrime, leftTangent, rightTangent)
|
||||||
|
maxError, splitPoint = computeMaxError(points, bezCurve, uPrime)
|
||||||
|
if maxError < error:
|
||||||
|
return [bezCurve]
|
||||||
|
u = uPrime
|
||||||
|
|
||||||
|
# Fitting failed -- split at max error point and fit recursively
|
||||||
|
beziers = []
|
||||||
|
centerTangent = normalize(points[splitPoint-1] - points[splitPoint+1])
|
||||||
|
beziers += fitCubic(points[:splitPoint+1], leftTangent, centerTangent, error)
|
||||||
|
beziers += fitCubic(points[splitPoint:], -centerTangent, rightTangent, error)
|
||||||
|
|
||||||
|
return beziers
|
||||||
|
|
||||||
|
|
||||||
|
def generateBezier(points, parameters, leftTangent, rightTangent):
|
||||||
|
bezCurve = [points[0], None, None, points[-1]]
|
||||||
|
|
||||||
|
# compute the A's
|
||||||
|
A = zeros((len(parameters), 2, 2))
|
||||||
|
for i, u in enumerate(parameters):
|
||||||
|
A[i][0] = leftTangent * 3*(1-u)**2 * u
|
||||||
|
A[i][1] = rightTangent * 3*(1-u) * u**2
|
||||||
|
|
||||||
|
# Create the C and X matrices
|
||||||
|
C = zeros((2, 2))
|
||||||
|
X = zeros(2)
|
||||||
|
|
||||||
|
for i, (point, u) in enumerate(zip(points, parameters)):
|
||||||
|
C[0][0] += dot(A[i][0], A[i][0])
|
||||||
|
C[0][1] += dot(A[i][0], A[i][1])
|
||||||
|
C[1][0] += dot(A[i][0], A[i][1])
|
||||||
|
C[1][1] += dot(A[i][1], A[i][1])
|
||||||
|
|
||||||
|
tmp = point - bezier.q([points[0], points[0], points[-1], points[-1]], u)
|
||||||
|
|
||||||
|
X[0] += dot(A[i][0], tmp)
|
||||||
|
X[1] += dot(A[i][1], tmp)
|
||||||
|
|
||||||
|
# Compute the determinants of C and X
|
||||||
|
det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1]
|
||||||
|
det_C0_X = C[0][0] * X[1] - C[1][0] * X[0]
|
||||||
|
det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1]
|
||||||
|
|
||||||
|
# Finally, derive alpha values
|
||||||
|
alpha_l = 0.0 if det_C0_C1 == 0 else det_X_C1 / det_C0_C1
|
||||||
|
alpha_r = 0.0 if det_C0_C1 == 0 else det_C0_X / det_C0_C1
|
||||||
|
|
||||||
|
# If alpha negative, use the Wu/Barsky heuristic (see text) */
|
||||||
|
# (if alpha is 0, you get coincident control points that lead to
|
||||||
|
# divide by zero in any subsequent NewtonRaphsonRootFind() call. */
|
||||||
|
segLength = linalg.norm(points[0] - points[-1])
|
||||||
|
epsilon = 1.0e-6 * segLength
|
||||||
|
if alpha_l < epsilon or alpha_r < epsilon:
|
||||||
|
# fall back on standard (probably inaccurate) formula, and subdivide further if needed.
|
||||||
|
bezCurve[1] = bezCurve[0] + leftTangent * (segLength / 3.0)
|
||||||
|
bezCurve[2] = bezCurve[3] + rightTangent * (segLength / 3.0)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# First and last control points of the Bezier curve are
|
||||||
|
# positioned exactly at the first and last data points
|
||||||
|
# Control points 1 and 2 are positioned an alpha distance out
|
||||||
|
# on the tangent vectors, left and right, respectively
|
||||||
|
bezCurve[1] = bezCurve[0] + leftTangent * alpha_l
|
||||||
|
bezCurve[2] = bezCurve[3] + rightTangent * alpha_r
|
||||||
|
|
||||||
|
return bezCurve
|
||||||
|
|
||||||
|
|
||||||
|
def reparameterize(bezier, points, parameters):
|
||||||
|
return [newtonRaphsonRootFind(bezier, point, u) for point, u in zip(points, parameters)]
|
||||||
|
|
||||||
|
|
||||||
|
def newtonRaphsonRootFind(bez, point, u):
|
||||||
|
"""
|
||||||
|
Newton's root finding algorithm calculates f(x)=0 by reiterating
|
||||||
|
x_n+1 = x_n - f(x_n)/f'(x_n)
|
||||||
|
|
||||||
|
We are trying to find curve parameter u for some point p that minimizes
|
||||||
|
the distance from that point to the curve. Distance point to curve is d=q(u)-p.
|
||||||
|
At minimum distance the point is perpendicular to the curve.
|
||||||
|
We are solving
|
||||||
|
f = q(u)-p * q'(u) = 0
|
||||||
|
with
|
||||||
|
f' = q'(u) * q'(u) + q(u)-p * q''(u)
|
||||||
|
|
||||||
|
gives
|
||||||
|
u_n+1 = u_n - |q(u_n)-p * q'(u_n)| / |q'(u_n)**2 + q(u_n)-p * q''(u_n)|
|
||||||
|
"""
|
||||||
|
d = bezier.q(bez, u)-point
|
||||||
|
numerator = (d * bezier.qprime(bez, u)).sum()
|
||||||
|
denominator = (bezier.qprime(bez, u)**2 + d * bezier.qprimeprime(bez, u)).sum()
|
||||||
|
|
||||||
|
if denominator == 0.0:
|
||||||
|
return u
|
||||||
|
else:
|
||||||
|
return u - numerator/denominator
|
||||||
|
|
||||||
|
|
||||||
|
def chordLengthParameterize(points):
|
||||||
|
u = [0.0]
|
||||||
|
for i in range(1, len(points)):
|
||||||
|
u.append(u[i-1] + linalg.norm(points[i] - points[i-1]))
|
||||||
|
|
||||||
|
for i, _ in enumerate(u):
|
||||||
|
u[i] = u[i] / u[-1]
|
||||||
|
|
||||||
|
return u
|
||||||
|
|
||||||
|
|
||||||
|
def computeMaxError(points, bez, parameters):
|
||||||
|
maxDist = 0.0
|
||||||
|
splitPoint = len(points)/2
|
||||||
|
for i, (point, u) in enumerate(zip(points, parameters)):
|
||||||
|
dist = linalg.norm(bezier.q(bez, u)-point)**2
|
||||||
|
if dist > maxDist:
|
||||||
|
maxDist = dist
|
||||||
|
splitPoint = i
|
||||||
|
|
||||||
|
return maxDist, splitPoint
|
||||||
|
|
||||||
|
|
||||||
|
def normalize(v):
|
||||||
|
return v / linalg.norm(v)
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,22 @@
|
||||||
|
use druid::widget::{Button, Flex, Label};
|
||||||
|
use druid::{AppLauncher, LocalizedString, PlatformError, Widget, WidgetExt, WindowDesc};
|
||||||
|
|
||||||
|
fn main() -> Result<(), PlatformError> {
|
||||||
|
let main_window = WindowDesc::new(ui_builder);
|
||||||
|
let data = 0_u32;
|
||||||
|
AppLauncher::with_window(main_window)
|
||||||
|
.use_simple_logger()
|
||||||
|
.launch(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui_builder() -> impl Widget<u32> {
|
||||||
|
// The label text will be computed dynamically based on the current locale and count
|
||||||
|
let text =
|
||||||
|
LocalizedString::new("hello-counter").with_arg("count", |data: &u32, _env| (*data).into());
|
||||||
|
let label = Label::new(text).padding(5.0).center();
|
||||||
|
let button = Button::new("increment")
|
||||||
|
.on_click(|_ctx, data, _env| *data += 1)
|
||||||
|
.padding(5.0);
|
||||||
|
|
||||||
|
Flex::column().with_child(label).with_child(button)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue