cleanup
This commit is contained in:
parent
87a967ad9c
commit
2d0f9f01d5
@ -12,16 +12,11 @@ compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : ang
|
|||||||
# -------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
immutable Shape
|
immutable Shape
|
||||||
# vertices::AVec
|
|
||||||
x::AVec
|
x::AVec
|
||||||
y::AVec
|
y::AVec
|
||||||
end
|
end
|
||||||
|
|
||||||
# Shape(x, y) = Shape(collect(zip(x, y)))
|
|
||||||
Shape(verts::AVec) = Shape(unzip(verts)...)
|
Shape(verts::AVec) = Shape(unzip(verts)...)
|
||||||
|
|
||||||
# get_xs(shape::Shape) = Float64[v[1] for v in shape.vertices]
|
|
||||||
# get_ys(shape::Shape) = Float64[v[2] for v in shape.vertices]
|
|
||||||
get_xs(shape::Shape) = shape.x
|
get_xs(shape::Shape) = shape.x
|
||||||
get_ys(shape::Shape) = shape.y
|
get_ys(shape::Shape) = shape.y
|
||||||
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
|
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
|
||||||
@ -128,7 +123,6 @@ end
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
# center(shape::Shape) = (mean(shape.x), mean(shape.y))
|
|
||||||
|
|
||||||
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
|
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
|
||||||
function center(shape::Shape)
|
function center(shape::Shape)
|
||||||
@ -206,108 +200,6 @@ function rotate(shape::Shape, Θ::Real, c = center(shape))
|
|||||||
rotate!(shapecopy, Θ, c)
|
rotate!(shapecopy, Θ, c)
|
||||||
end
|
end
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# # abstract AbstractAxisTicks
|
|
||||||
# # immutable DefaultAxisTicks end
|
|
||||||
# #
|
|
||||||
# # type CustomAxisTicks
|
|
||||||
# # # TODO
|
|
||||||
# # end
|
|
||||||
#
|
|
||||||
# # simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
|
|
||||||
# type Axis
|
|
||||||
# d::KW
|
|
||||||
# # name::AbstractString # "x" or "y"
|
|
||||||
# # label::AbstractString
|
|
||||||
# # lims::NTuple{2}
|
|
||||||
# # ticks::AbstractAxisTicks
|
|
||||||
# # scale::Symbol
|
|
||||||
# # flip::Bool
|
|
||||||
# # rotation::Number
|
|
||||||
# # guidefont::Font
|
|
||||||
# # tickfont::Font
|
|
||||||
# # use_minor::Bool
|
|
||||||
# # _plotDefaults[:foreground_color_axis] = :match # axis border/tick colors
|
|
||||||
# # _plotDefaults[:foreground_color_border] = :match # plot area border/spines
|
|
||||||
# # _plotDefaults[:foreground_color_text] = :match # tick text color
|
|
||||||
# # _plotDefaults[:foreground_color_guide] = :match # guide text color
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # function processAxisArg(d::KW, letter::AbstractString, arg)
|
|
||||||
# function axis(letter, args...; kw...)
|
|
||||||
# # TODO: this should initialize with values from _plotDefaults
|
|
||||||
# d = KW(
|
|
||||||
# :letter => letter,
|
|
||||||
# :label => "",
|
|
||||||
# :lims => :auto,
|
|
||||||
# :ticks => :auto,
|
|
||||||
# :scale => :identity,
|
|
||||||
# :flip => false,
|
|
||||||
# :rotation => 0,
|
|
||||||
# :guidefont => font(11),
|
|
||||||
# :tickfont => font(8),
|
|
||||||
# :use_minor => false,
|
|
||||||
# :foreground_color_axis => :match,
|
|
||||||
# :foreground_color_border => :match,
|
|
||||||
# :foreground_color_text => :match,
|
|
||||||
# :foreground_color_guide => :match,
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# # first process args
|
|
||||||
# for arg in args
|
|
||||||
# T = typeof(arg)
|
|
||||||
# arg = get(_scaleAliases, arg, arg)
|
|
||||||
# # scale, flip, label, lim, tick = axis_symbols(letter, "scale", "flip", "label", "lims", "ticks")
|
|
||||||
#
|
|
||||||
# if typeof(arg) <: Font
|
|
||||||
# d[:tickfont] = arg
|
|
||||||
# d[:guidefont] = arg
|
|
||||||
#
|
|
||||||
# elseif arg in _allScales
|
|
||||||
# d[:scale] = arg
|
|
||||||
#
|
|
||||||
# elseif arg in (:flip, :invert, :inverted)
|
|
||||||
# d[:flip] = true
|
|
||||||
#
|
|
||||||
# elseif T <: @compat(AbstractString)
|
|
||||||
# d[:label] = arg
|
|
||||||
#
|
|
||||||
# # xlims/ylims
|
|
||||||
# elseif (T <: Tuple || T <: AVec) && length(arg) == 2
|
|
||||||
# sym = typeof(arg[1]) <: Number ? :lims : :ticks
|
|
||||||
# d[sym] = arg
|
|
||||||
#
|
|
||||||
# # xticks/yticks
|
|
||||||
# elseif T <: AVec
|
|
||||||
# d[:ticks] = arg
|
|
||||||
#
|
|
||||||
# elseif arg == nothing
|
|
||||||
# d[:ticks] = []
|
|
||||||
#
|
|
||||||
# elseif typeof(arg) <: Number
|
|
||||||
# d[:rotation] = arg
|
|
||||||
#
|
|
||||||
# else
|
|
||||||
# warn("Skipped $(letter)axis arg $arg")
|
|
||||||
#
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# # then override for any keywords
|
|
||||||
# for (k,v) in kw
|
|
||||||
# d[k] = v
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Axis(d)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# xaxis(args...) = axis("x", args...)
|
|
||||||
# yaxis(args...) = axis("y", args...)
|
|
||||||
# zaxis(args...) = axis("z", args...)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@ -378,58 +270,10 @@ type Axis #<: Associative{Symbol,Any}
|
|||||||
d::KW
|
d::KW
|
||||||
end
|
end
|
||||||
|
|
||||||
function expand_extrema!(a::Axis, v::Number)
|
xaxis(args...) = Axis("x", args...)
|
||||||
emin, emax = a[:extrema]
|
yaxis(args...) = Axis("y", args...)
|
||||||
a[:extrema] = (min(v, emin), max(v, emax))
|
zaxis(args...) = Axis("z", args...)
|
||||||
end
|
|
||||||
function expand_extrema!{MIN<:Number,MAX<:Number}(a::Axis, v::Tuple{MIN,MAX})
|
|
||||||
emin, emax = a[:extrema]
|
|
||||||
a[:extrema] = (min(v[1], emin), max(v[2], emax))
|
|
||||||
end
|
|
||||||
function expand_extrema!{N<:Number}(a::Axis, v::AVec{N})
|
|
||||||
if !isempty(v)
|
|
||||||
emin, emax = a[:extrema]
|
|
||||||
a[:extrema] = (min(minimum(v), emin), max(maximum(v), emax))
|
|
||||||
end
|
|
||||||
a[:extrema]
|
|
||||||
end
|
|
||||||
|
|
||||||
# these methods track the discrete values which correspond to axis continuous values (cv)
|
|
||||||
# whenever we have discrete values, we automatically set the ticks to match.
|
|
||||||
# we return the plot value
|
|
||||||
function discrete_value!(a::Axis, v)
|
|
||||||
cv = get(a[:discrete_map], v, NaN)
|
|
||||||
if isnan(cv)
|
|
||||||
emin, emax = a[:extrema]
|
|
||||||
cv = max(0.5, emax + 1.0)
|
|
||||||
expand_extrema!(a, cv)
|
|
||||||
a[:discrete_map][v] = cv
|
|
||||||
push!(a[:discrete_values], (cv, v))
|
|
||||||
end
|
|
||||||
cv
|
|
||||||
end
|
|
||||||
|
|
||||||
# add the discrete value for each item
|
|
||||||
function discrete_value!(a::Axis, v::AVec)
|
|
||||||
Float64[discrete_value!(a, vi) for vi=v]
|
|
||||||
end
|
|
||||||
|
|
||||||
Base.show(io::IO, a::Axis) = dumpdict(a.d, "Axis", true)
|
|
||||||
Base.getindex(a::Axis, k::Symbol) = getindex(a.d, k)
|
|
||||||
Base.setindex!(a::Axis, v, ks::Symbol...) = setindex!(a.d, v, ks...)
|
|
||||||
Base.haskey(a::Axis, k::Symbol) = haskey(a.d, k)
|
|
||||||
Base.extrema(a::Axis) = a[:extrema]
|
|
||||||
|
|
||||||
# get discrete ticks, or not
|
|
||||||
function get_ticks(a::Axis)
|
|
||||||
ticks = a[:ticks]
|
|
||||||
dvals = a[:discrete_values]
|
|
||||||
if !isempty(dvals) && ticks == :auto
|
|
||||||
vals, labels = unzip(dvals)
|
|
||||||
else
|
|
||||||
ticks
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
const _axis_symbols = (:label, :lims, :ticks, :scale, :flip, :rotation)
|
const _axis_symbols = (:label, :lims, :ticks, :scale, :flip, :rotation)
|
||||||
const _axis_symbols_fonts_colors = (
|
const _axis_symbols_fonts_colors = (
|
||||||
@ -440,23 +284,10 @@ const _axis_symbols_fonts_colors = (
|
|||||||
:foreground_color_guide
|
:foreground_color_guide
|
||||||
)
|
)
|
||||||
|
|
||||||
# function processAxisArg(d::KW, letter::AbstractString, arg)
|
|
||||||
function Axis(letter::AbstractString, args...; kw...)
|
function Axis(letter::AbstractString, args...; kw...)
|
||||||
# init with defaults
|
# init with values from _plotDefaults
|
||||||
d = KW(
|
d = KW(
|
||||||
:letter => letter,
|
:letter => letter,
|
||||||
# :label => "",
|
|
||||||
# :lims => :auto,
|
|
||||||
# :ticks => :auto,
|
|
||||||
# :scale => :identity,
|
|
||||||
# :flip => false,
|
|
||||||
# :rotation => 0,
|
|
||||||
# :guidefont => font(11),
|
|
||||||
# :tickfont => font(8),
|
|
||||||
# :foreground_color_axis => :match,
|
|
||||||
# :foreground_color_border => :match,
|
|
||||||
# :foreground_color_text => :match,
|
|
||||||
# :foreground_color_guide => :match,
|
|
||||||
:extrema => (Inf, -Inf),
|
:extrema => (Inf, -Inf),
|
||||||
:discrete_map => Dict(), # map discrete values to continuous plot values
|
:discrete_map => Dict(), # map discrete values to continuous plot values
|
||||||
:discrete_values => Tuple{Float64,Any}[],
|
:discrete_values => Tuple{Float64,Any}[],
|
||||||
@ -471,7 +302,14 @@ function Axis(letter::AbstractString, args...; kw...)
|
|||||||
d[k] = _plotDefaults[k]
|
d[k] = _plotDefaults[k]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# update the defaults
|
||||||
|
update!(Axis(d), args...; kw...)
|
||||||
|
end
|
||||||
|
|
||||||
|
# update an Axis object with magic args and keywords
|
||||||
|
function update!(a::Axis, args..., kw...)
|
||||||
# first process args
|
# first process args
|
||||||
|
d = a.d
|
||||||
for arg in args
|
for arg in args
|
||||||
T = typeof(arg)
|
T = typeof(arg)
|
||||||
arg = get(_scaleAliases, arg, arg)
|
arg = get(_scaleAliases, arg, arg)
|
||||||
@ -518,14 +356,63 @@ function Axis(letter::AbstractString, args...; kw...)
|
|||||||
d[sym] = v
|
d[sym] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
a
|
||||||
Axis(d)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
xaxis(args...) = Axis("x", args...)
|
Base.show(io::IO, a::Axis) = dumpdict(a.d, "Axis", true)
|
||||||
yaxis(args...) = Axis("y", args...)
|
Base.getindex(a::Axis, k::Symbol) = getindex(a.d, k)
|
||||||
zaxis(args...) = Axis("z", args...)
|
Base.setindex!(a::Axis, v, ks::Symbol...) = setindex!(a.d, v, ks...)
|
||||||
|
Base.haskey(a::Axis, k::Symbol) = haskey(a.d, k)
|
||||||
|
Base.extrema(a::Axis) = a[:extrema]
|
||||||
|
|
||||||
|
# get discrete ticks, or not
|
||||||
|
function get_ticks(a::Axis)
|
||||||
|
ticks = a[:ticks]
|
||||||
|
dvals = a[:discrete_values]
|
||||||
|
if !isempty(dvals) && ticks == :auto
|
||||||
|
vals, labels = unzip(dvals)
|
||||||
|
else
|
||||||
|
ticks
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function expand_extrema!(a::Axis, v::Number)
|
||||||
|
emin, emax = a[:extrema]
|
||||||
|
a[:extrema] = (min(v, emin), max(v, emax))
|
||||||
|
end
|
||||||
|
function expand_extrema!{MIN<:Number,MAX<:Number}(a::Axis, v::Tuple{MIN,MAX})
|
||||||
|
emin, emax = a[:extrema]
|
||||||
|
a[:extrema] = (min(v[1], emin), max(v[2], emax))
|
||||||
|
end
|
||||||
|
function expand_extrema!{N<:Number}(a::Axis, v::AVec{N})
|
||||||
|
if !isempty(v)
|
||||||
|
emin, emax = a[:extrema]
|
||||||
|
a[:extrema] = (min(minimum(v), emin), max(maximum(v), emax))
|
||||||
|
end
|
||||||
|
a[:extrema]
|
||||||
|
end
|
||||||
|
|
||||||
|
# these methods track the discrete values which correspond to axis continuous values (cv)
|
||||||
|
# whenever we have discrete values, we automatically set the ticks to match.
|
||||||
|
# we return the plot value
|
||||||
|
function discrete_value!(a::Axis, v)
|
||||||
|
cv = get(a[:discrete_map], v, NaN)
|
||||||
|
if isnan(cv)
|
||||||
|
emin, emax = a[:extrema]
|
||||||
|
cv = max(0.5, emax + 1.0)
|
||||||
|
expand_extrema!(a, cv)
|
||||||
|
a[:discrete_map][v] = cv
|
||||||
|
push!(a[:discrete_values], (cv, v))
|
||||||
|
end
|
||||||
|
cv
|
||||||
|
end
|
||||||
|
|
||||||
|
# add the discrete value for each item
|
||||||
|
function discrete_value!(a::Axis, v::AVec)
|
||||||
|
Float64[discrete_value!(a, vi) for vi=v]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
@ -537,10 +424,6 @@ immutable Stroke
|
|||||||
end
|
end
|
||||||
|
|
||||||
function stroke(args...; alpha = nothing)
|
function stroke(args...; alpha = nothing)
|
||||||
# defaults
|
|
||||||
# width = 1
|
|
||||||
# color = colorant"black"
|
|
||||||
# style = :solid
|
|
||||||
width = nothing
|
width = nothing
|
||||||
color = nothing
|
color = nothing
|
||||||
style = nothing
|
style = nothing
|
||||||
@ -557,10 +440,8 @@ function stroke(args...; alpha = nothing)
|
|||||||
try
|
try
|
||||||
color = parse(Colorant, string(arg))
|
color = parse(Colorant, string(arg))
|
||||||
end
|
end
|
||||||
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
|
|
||||||
elseif allAlphas(arg)
|
elseif allAlphas(arg)
|
||||||
alpha = arg
|
alpha = arg
|
||||||
# elseif typeof(arg) <: Real
|
|
||||||
elseif allReals(arg)
|
elseif allReals(arg)
|
||||||
width = arg
|
width = arg
|
||||||
else
|
else
|
||||||
@ -579,9 +460,6 @@ immutable Brush
|
|||||||
end
|
end
|
||||||
|
|
||||||
function brush(args...; alpha = nothing)
|
function brush(args...; alpha = nothing)
|
||||||
# defaults
|
|
||||||
# sz = 1
|
|
||||||
# color = colorant"black"
|
|
||||||
size = nothing
|
size = nothing
|
||||||
color = nothing
|
color = nothing
|
||||||
|
|
||||||
@ -594,10 +472,8 @@ function brush(args...; alpha = nothing)
|
|||||||
try
|
try
|
||||||
color = parse(Colorant, string(arg))
|
color = parse(Colorant, string(arg))
|
||||||
end
|
end
|
||||||
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
|
|
||||||
elseif allAlphas(arg)
|
elseif allAlphas(arg)
|
||||||
alpha = arg
|
alpha = arg
|
||||||
# elseif typeof(arg) <: Real
|
|
||||||
elseif allReals(arg)
|
elseif allReals(arg)
|
||||||
size = arg
|
size = arg
|
||||||
else
|
else
|
||||||
@ -626,8 +502,6 @@ abstract AbstractSurface
|
|||||||
|
|
||||||
"represents a contour or surface mesh"
|
"represents a contour or surface mesh"
|
||||||
immutable Surface{M<:AMat} <: AbstractSurface
|
immutable Surface{M<:AMat} <: AbstractSurface
|
||||||
# x::AVec
|
|
||||||
# y::AVec
|
|
||||||
surf::M
|
surf::M
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -717,59 +591,55 @@ end
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
# @require FixedSizeArrays begin
|
type BezierCurve{T <: FixedSizeArrays.Vec}
|
||||||
|
control_points::Vector{T}
|
||||||
|
end
|
||||||
|
|
||||||
type BezierCurve{T <: FixedSizeArrays.Vec}
|
function Base.call(bc::BezierCurve, t::Real)
|
||||||
control_points::Vector{T}
|
p = zero(P2)
|
||||||
|
n = length(bc.control_points)-1
|
||||||
|
for i in 0:n
|
||||||
|
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
|
||||||
end
|
end
|
||||||
|
p
|
||||||
|
end
|
||||||
|
|
||||||
function Base.call(bc::BezierCurve, t::Real)
|
Base.mean(x::Real, y::Real) = 0.5*(x+y)
|
||||||
p = zero(P2)
|
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
|
||||||
n = length(bc.control_points)-1
|
|
||||||
for i in 0:n
|
|
||||||
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
|
|
||||||
end
|
|
||||||
p
|
|
||||||
end
|
|
||||||
|
|
||||||
Base.mean(x::Real, y::Real) = 0.5*(x+y)
|
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
||||||
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
|
|
||||||
|
|
||||||
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.
|
||||||
|
# may create a loop if necessary. Assumes the view is [0,1]
|
||||||
|
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
|
||||||
|
mn = mean(p, q)
|
||||||
|
diff = q - p
|
||||||
|
|
||||||
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
|
minx, maxx = minimum(xview), maximum(xview)
|
||||||
# may create a loop if necessary. Assumes the view is [0,1]
|
miny, maxy = minimum(yview), maximum(yview)
|
||||||
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
|
diffpct = P2(diff[1] / (maxx - minx),
|
||||||
mn = mean(p, q)
|
diff[2] / (maxy - miny))
|
||||||
diff = q - p
|
|
||||||
|
|
||||||
minx, maxx = minimum(xview), maximum(xview)
|
# these points give the initial/final "rise"
|
||||||
miny, maxy = minimum(yview), maximum(yview)
|
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
|
||||||
diffpct = P2(diff[1] / (maxx - minx),
|
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
|
||||||
diff[2] / (maxy - miny))
|
upper_control = p + vertical_offset
|
||||||
|
lower_control = q - vertical_offset
|
||||||
|
|
||||||
# these points give the initial/final "rise"
|
# try to figure out when to loop around vs just connecting straight
|
||||||
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
|
# TODO: choose loop direction based on sign of p[1]??
|
||||||
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
|
# x_close_together = abs(diffpct[1]) <= 0.05
|
||||||
upper_control = p + vertical_offset
|
p_is_higher = diff[2] <= 0
|
||||||
lower_control = q - vertical_offset
|
inside_control_points = if p_is_higher
|
||||||
|
# add curve points which will create a loop
|
||||||
|
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
|
||||||
|
inside_offset = P2(0.3 * (maxx - minx), 0)
|
||||||
|
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
|
||||||
|
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
|
||||||
|
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
# try to figure out when to loop around vs just connecting straight
|
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
|
||||||
# TODO: choose loop direction based on sign of p[1]??
|
end
|
||||||
# x_close_together = abs(diffpct[1]) <= 0.05
|
|
||||||
p_is_higher = diff[2] <= 0
|
|
||||||
inside_control_points = if p_is_higher
|
|
||||||
# add curve points which will create a loop
|
|
||||||
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
|
|
||||||
inside_offset = P2(0.3 * (maxx - minx), 0)
|
|
||||||
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
|
|
||||||
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
|
|
||||||
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
|
|
||||||
end
|
|
||||||
|
|
||||||
# end
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user