generalize curves

This commit is contained in:
Thomas Breloff 2016-02-22 10:39:03 -05:00
parent 9da4083096
commit 6b61c5900c

View File

@ -249,22 +249,22 @@ end
export export
P2, P2,
P3, P3,
points,
BezierCurve, BezierCurve,
curve_points,
directed_curve directed_curve
typealias P2 FixedSizeArrays.Vec{2,Float64} typealias P2 FixedSizeArrays.Vec{2,Float64}
typealias P3 FixedSizeArrays.Vec{3,Float64} typealias P3 FixedSizeArrays.Vec{3,Float64}
type BezierCurve{T <: FixedSizeArrays.Vec} type BezierCurve{T <: FixedSizeArrays.Vec}
points::Vector{T} control_points::Vector{T}
end end
function Base.call(bc::BezierCurve, t::Real) function Base.call(bc::BezierCurve, t::Real)
p = zero(P2) p = zero(P2)
n = length(bc.points)-1 n = length(bc.control_points)-1
for i in 0:n for i in 0:n
p += bc.points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
end end
p p
end end
@ -272,32 +272,38 @@ end
Base.mean(x::Real, y::Real) = 0.5*(x+y) Base.mean(x::Real, y::Real) = 0.5*(x+y)
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps) Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
points(curve::BezierCurve, n::Integer = 50) = map(curve, linspace(0,1,n)) curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards. # build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
# may create a loop if necessary # may create a loop if necessary. Assumes the view is [0,1]
function directed_curve(p::P2, q::P2) function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
mn = mean(p,q) mn = mean(p, q)
diff = q - p
minx, maxx = minimum(xview), maximum(xview)
miny, maxy = minimum(yview), maximum(yview)
diffpct = P2(diff[1] / (maxx - minx),
diff[2] / (maxy - miny))
# these points give the initial/final "rise" # these points give the initial/final "rise"
yoffset = max(0.4, min(1.0, abs(mn[2]-p[2]))) vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
firstoffset = P2(0, yoffset) upper_control = p + vertical_offset
uppery = p + firstoffset lower_control = q - vertical_offset
lowery = q - firstoffset
# try to figure out when to loop around vs just connecting straight # try to figure out when to loop around vs just connecting straight
# TODO: choose loop direction based on sign of p[1]?? # TODO: choose loop direction based on sign of p[1]??
insideoffset = P2(0.5, 0) x_close_together = abs(diffpct[1]) <= 0.05
inside = [] p_is_higher = diff[2] <= 0
x_close_together = abs(p[1] - q[1]) <= 0.1 inside_control_points = if x_close_together && p_is_higher
p_is_higher = p[2] >= q[2]
if x_close_together && p_is_higher
# add curve points which will create a loop # add curve points which will create a loop
sgn = p[1] < 0 ? -1 : 1 sgn = p[1] < 0.5 * (maxx + minx) ? -1 : 1
inside = [uppery + sgn * insideoffset, lowery + sgn * insideoffset] inside_offset = P2(0.3 * (maxx - minx), 0)
[upper_control + sgn * inside_offset, lower_control + sgn * inside_offset]
else
[]
end end
BezierCurve([p, uppery, inside..., lowery, q]) BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
end end
end end