-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding bspline function in polygons.jl (evaluates npoints B-spline curve based on control points) #305
Conversation
B-Spline function with clamped/unclamped options using [De Boor's Algorithm](https://en.wikipedia.org/wiki/De_Boor%27s_algorithm).
@@ -775,6 +775,76 @@ function polyfit(plist::Array{Point,1}, npoints = 30) | |||
return resultpoly | |||
end | |||
|
|||
""" | |||
bspline(controlPoints::Array{Point,1}, npoints; degree=3, clamped=true) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this creates a polygon, the name could start with poly
to help people find it: polyspline
? polybspline
and to be consistent with all the other ones. But bspline
could be OK too...
controlpoints
or control_points
is more Julian than camel-lycontrollPoints
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
polybspline
makes sense since we're discussing adding other spline types I guess. Also ugh I yeah you're right for some reason the Julia community hates capital letters lol
src/polygons.jl
Outdated
npoints <= 0 && error("Error: npoints must be greater than zero.") | ||
degree <= 0 && error("Error: degree must be greater than zero.") | ||
degree >= nCP && error("Error: degree cannot be greater than the number of control points.") | ||
points = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be points = Point[]
- might be faster
src/polygons.jl
Outdated
degree <= 0 && error("Error: degree must be greater than zero.") | ||
degree >= nCP && error("Error: degree cannot be greater than the number of control points.") | ||
points = [] | ||
T = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
T = Float64[]
?
src/polygons.jl
Outdated
p: Degree of B-spline. | ||
""" | ||
function deBoor(k,x,t,c,p) | ||
d = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d = Float64[]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, not necessarily. It can be d = Point[]
which will satisfy most use cases. If we want to keep it capable of dealing with Float and other arithmetic data types, we'd have to keep it as []
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it could be a Union? But I'm guessing the call to this internal function will always be using Points? I know that Any[]
arrays are not recommended for performance reasons...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure of whether having a union would provide any performance benefits over an Any[]. I think the most obvious way to maintain generality and performance is to make sure that the dot notation over Point as a tuple. That way the function can take in a Point[] and do both x and y calculations separately. But then again we'd lose out a lot of the contiguous memory accesses and vector operations. Do you know if there a way to make Julia precompile for two different possible data types (i.e. tell it it's either going to be a Float64[] or Point[])?
src/polygons.jl
Outdated
for i=0:npoints | ||
t=i/npoints | ||
if !clamped | ||
t = t*(T[nCP-degree]-T[degree+1])+T[degree+1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
t *=
should be OK/better?
src/polygons.jl
Outdated
end | ||
k=1 #index of knot interval | ||
while k < nCP | ||
if (t<T[k+1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if t < T[k+1]
is OK
src/polygons.jl
Outdated
end | ||
k += 1 | ||
end | ||
println("t: ",t," k: ",k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could have @debug "t: ",t," k: ",k
if you needed to keep some debugging in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just forgot to remove that line😅
This is great. Thanks very much! Other things to do:
|
As for
perhaps just use https://github.com/JeffreySarnoff/CatmullRom.jl ? |
Thank you for the great PR!
Yeah, the performance can be improved. Here are comparisons with my package, BasicBSpline.jl: julia> using Luxor, BasicBSpline, BenchmarkTools
julia> ps = [Point(randn(), randn()) for _ in 1:8] # control points
8-element Vector{Point}:
Point(-0.7994182143543495, 0.9390793075737545)
Point(-0.22753695196095303, -0.7882913054541987)
Point(1.2845720190835004, 0.5623233646865434)
Point(-1.8355529584037287, 0.8438730777812197)
Point(0.5510575401065174, 1.402746732606732)
Point(0.45539845640833243, -1.265031994706883)
Point(-0.1908845986894419, -0.10172057455987063)
Point(-1.386438336193102, -0.29019694915633065)
julia> P = BSplineSpace{3}(KnotVector([0,0,0,0,1,2,3,4,5,5,5,5]))
BSplineSpace{3, Int64, KnotVector{Int64}}(KnotVector([0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5]))
julia> C = BSplineManifold(ps, P)
BSplineManifold{1, (3,), Point, Int64, Tuple{BSplineSpace{3, Int64, KnotVector{Int64}}}}(Point[Point(-0.7994182143543495, 0.9390793075737545), Point(-0.22753695196095303, -0.7882913054541987), Point(1.2845720190835004, 0.5623233646865434), Point(-1.8355529584037287, 0.8438730777812197), Point(0.5510575401065174, 1.402746732606732), Point(0.45539845640833243, -1.265031994706883), Point(-0.1908845986894419, -0.10172057455987063), Point(-1.386438336193102, -0.29019694915633065)], (BSplineSpace{3, Int64, KnotVector{Int64}}(KnotVector([0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5])),))
julia> C.(range(domain(P), length=21))
21-element Vector{Point}:
Point(-0.7994182143543495, 0.9390793075737545)
Point(-0.35288449596438315, 0.051968983954802477)
Point(0.0612559346484744, -0.18664398382581504)
Point(0.34154483103380284, -0.029266470466635504)
Point(0.38652394674118207, 0.2715946493338039)
Point(0.1400172011610212, 0.5107818139356997)
Point(-0.27302282295295666, 0.6725347139341815)
Point(-0.6823613770061979, 0.7884423529831135)
Point(-0.9177637124041496, 0.890093734736359)
Point(-0.8581722393237627, 0.99468809056206)
Point(-0.5792380030280005, 1.0618655626854696)
Point(-0.205789207551331, 1.0368765210461206)
Point(0.1373459430717789, 0.864971335583544)
Point(0.35033275819609594, 0.5204195808298326)
Point(0.4333106007333227, 0.09356764968732044)
Point(0.41141234698439616, -0.29621886034909783)
Point(0.309770873250253, -0.5295743517845274)
Point(0.13797423332130898, -0.5305523328668529)
Point(-0.1565688090541049, -0.3968827348150751)
Point(-0.6419943126381785, -0.26971459459097424)
Point(-1.386438336193102, -0.29019694915633065)
julia> Luxor.bspline(ps, 20) # The same results as previous one (except for floating point errors)
21-element Vector{Any}:
Point(-0.7994182143543495, 0.9390793075737545)
Point(-0.35288449596438315, 0.0519689839548025)
Point(0.061255934648474314, -0.18664398382581504)
Point(0.3415448310338027, -0.029266470466635615)
Point(0.3865239467411821, 0.27159464933380395)
Point(0.14001720116102095, 0.5107818139356994)
Point(-0.27302282295295643, 0.6725347139341815)
Point(-0.6823613770061976, 0.7884423529831134)
Point(-0.9177637124041498, 0.8900937347363591)
Point(-0.8581722393237624, 0.9946880905620599)
Point(-0.5792380030280007, 1.0618655626854698)
Point(-0.2057892075513309, 1.0368765210461206)
Point(0.13734594307177872, 0.8649713355835442)
Point(0.3503327581960958, 0.5204195808298324)
Point(0.4333106007333225, 0.09356764968732088)
Point(0.41141234698439616, -0.29621886034909783)
Point(0.30977087325025293, -0.5295743517845275)
Point(0.1379742333213091, -0.5305523328668529)
Point(-0.15656880905410495, -0.39688273481507497)
Point(-0.6419943126381779, -0.26971459459097435)
Point(-1.386438336193102, -0.29019694915633065)
julia> @benchmark C.(range(domain(P), length=21))
BenchmarkTools.Trial: 10000 samples with 23 evaluations.
Range (min … max): 950.913 ns … 219.537 μs ┊ GC (min … max): 0.00% … 98.23%
Time (median): 1.001 μs ┊ GC (median): 0.00%
Time (mean ± σ): 1.156 μs ± 2.916 μs ┊ GC (mean ± σ): 3.50% ± 1.39%
▂▆██▆▄▂ ▁▂▂▁▁▁▃▄▄▃▂▁▁ ▁ ▁▁▁▁▂▂▂▂▂▂▁ ▁▁ ▂
████████████████████████████████████████████████▇▇▇▆▆▅▆▆▆▅▄▅▅ █
951 ns Histogram: log(frequency) by time 1.79 μs <
Memory estimate: 576 bytes, allocs estimate: 4.
julia> @benchmark Luxor.bspline(ps, 20) # I have commented out `println("t: ",t," k: ",k)` in this PR.
BenchmarkTools.Trial: 10000 samples with 1 evaluation.
Range (min … max): 16.361 μs … 3.592 ms ┊ GC (min … max): 0.00% … 97.29%
Time (median): 18.024 μs ┊ GC (median): 0.00%
Time (mean ± σ): 22.123 μs ± 66.233 μs ┊ GC (mean ± σ): 6.41% ± 2.16%
▂▇██▆▅▄▃▄▄▄▃▂▁▁ ▁▁▁▁▂▂▂▂▃▄▃▃▂▂▂▂▁▁ ▂
█████████████████████████████████████████▇▇▆▆▆▆▅▆▆▆▆▅▅▅▄▄▄▅ █
16.4 μs Histogram: log(frequency) by time 39.5 μs <
Memory estimate: 24.31 KiB, allocs estimate: 908. I'm not sure where the performance difference comes from, but one reason would be type-instability as commented in #305 (comment). (Note that BasicBSpline.jl does not use vectorization/parallelization explicitly.) As a maintainer of BasicBSpline.jl, I'm happy to help updating the code with BasicBSpline.jl. But there are more B-spline packages in Julia ecosystem, so another package might be better choice. One possible concern with BasicBSpline.jl would be increased load-time. julia> @time using Luxor
0.567440 seconds (546.01 k allocations: 39.472 MiB, 11.75% gc time, 3.79% compilation time)
julia> @time using BasicBSpline
0.347235 seconds (334.66 k allocations: 17.003 MiB) In any cases, avoiding reinventing the wheel is advisable for better interoperability with other packages and enhanced performance. |
A B-spline curve can be split into Bezier curves, so I think adding a method to generate a julia> ps # Defined randomly as in the previous comment
8-element Vector{Point}:
Point(-0.7994182143543495, 0.9390793075737545)
Point(-0.22753695196095303, -0.7882913054541987)
Point(1.2845720190835004, 0.5623233646865434)
Point(-1.8355529584037287, 0.8438730777812197)
Point(0.5510575401065174, 1.402746732606732)
Point(0.45539845640833243, -1.265031994706883)
Point(-0.1908845986894419, -0.10172057455987063)
Point(-1.386438336193102, -0.29019694915633065)
julia> P = BSplineSpace{3}(KnotVector([0,0,0,0,1,2,3,4,5,5,5,5]))
BSplineSpace{3, Int64, KnotVector{Int64}}(KnotVector([0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5]))
julia> C = BSplineManifold(ps, P) # Same curve definition as in the previous comment
BSplineManifold{1, (3,), Point, Int64, Tuple{BSplineSpace{3, Int64, KnotVector{Int64}}}}(Point[Point(-0.7994182143543495, 0.9390793075737545), Point(-0.22753695196095303, -0.7882913054541987), Point(1.2845720190835004, 0.5623233646865434), Point(-1.8355529584037287, 0.8438730777812197), Point(0.5510575401065174, 1.402746732606732), Point(0.45539845640833243, -1.265031994706883), Point(-0.1908845986894419, -0.10172057455987063), Point(-1.386438336193102, -0.29019694915633065)], (BSplineSpace{3, Int64, KnotVector{Int64}}(KnotVector([0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 5, 5])),))
julia> P2 = BSplineSpace{3}(unique(knotvector(P)) * 4)
BSplineSpace{3, Int64, KnotVector{Int64}}(KnotVector([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5]))
julia> C2 = refinement(C, P2)
BSplineManifold{1, (3,), Point, Int64, Tuple{BSplineSpace{3, Int64, KnotVector{Int64}}}}(Point[Point(-0.7994182143543495, 0.9390793075737545), Point(-0.22753695196095303, -0.7882913054541987), Point(0.5285175335612737, -0.11298397038382763), Point(0.3865239467411823, 0.27159464933380395), Point(0.3865239467411823, 0.27159464933380395), Point(0.24453035992109085, 0.6561732690514355), Point(-0.7955112992413191, 0.7500231734163277), Point(-0.9177637124041496, 0.8900937347363591), Point(-0.9177637124041496, 0.8900937347363591), Point(-1.0400161255669802, 1.0301642960563906), Point(-0.24447929273023133, 1.2164555143315614), Point(0.1373459430717789, 0.8649713355835442), Point(0.1373459430717789, 0.8649713355835442), Point(0.5191711788737892, 0.513487156835527), Point(0.4872848176410608, -0.37577241893567803), Point(0.309770873250253, -0.5295743517845274), Point(0.309770873250253, -0.5295743517845274), Point(0.13225692885944523, -0.6833762846333767), Point(-0.1908845986894419, -0.10172057455987063), Point(-1.386438336193102, -0.29019694915633065)], (BSplineSpace{3, Int64, KnotVector{Int64}}(KnotVector([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5])),))
julia> BezierPath([BezierPathSegment(controlpoints(C2)[1+4i:4+4i]) for i in 0:4])
5-element BezierPath:
[Point(-0.7994182143543495, 0.9390793075737545), Point(-0.22753695196095303, -0.7882913054541987), Point(0.5285175335612737, -0.11298397038382763), Point(0.3865239467411823, 0.27159464933380395)]
[Point(0.3865239467411823, 0.27159464933380395), Point(0.24453035992109085, 0.6561732690514355), Point(-0.7955112992413191, 0.7500231734163277), Point(-0.9177637124041496, 0.8900937347363591)]
[Point(-0.9177637124041496, 0.8900937347363591), Point(-1.0400161255669802, 1.0301642960563906), Point(-0.24447929273023133, 1.2164555143315614), Point(0.1373459430717789, 0.8649713355835442)]
[Point(0.1373459430717789, 0.8649713355835442), Point(0.5191711788737892, 0.513487156835527), Point(0.4872848176410608, -0.37577241893567803), Point(0.309770873250253, -0.5295743517845274)]
[Point(0.309770873250253, -0.5295743517845274), Point(0.13225692885944523, -0.6833762846333767), Point(-0.1908845986894419, -0.10172057455987063), Point(-1.386438336193102, -0.29019694915633065)] The curves using StaticArrays, Plots
# We need to convert the type of control points from `Luxor.Point` to `StaticArrays.SVector{2, Float64}` to plot the curves.
plot(BSplineManifold(SVector{2}.(Tuple.(controlpoints(C))), bsplinespaces(C)), xlims=(-2,2), ylims=(-2,2), label="original B-spline curve")
plot(BSplineManifold(SVector{2}.(Tuple.(controlpoints(C2))), bsplinespaces(C2)), xlims=(-2,2), ylims=(-2,2), label="B-spline curve with Bezier control points") An example script to plot the # Invert around the y-axis because Luxor and Plots have different coordinate systems.
# Magnify the points by `*100` to update the size of the curve.
ps2 = [Point(p.x, -p.y)*100 for p in controlpoints(C2)]
bezpath = BezierPath([BezierPathSegment(ps2[1+4i:4+4i]) for i in 0:4])
@svg begin
sethue(1.0, 0.0, 0.0)
drawbezierpath(bezpath, action = :stroke, close=true)
sethue(0.5, 0.5, 0.5)
circle.(ps2, 3, action = :fill)
poly(ps2, action = :stroke, close=false)
end |
@hyrodium Thanks for your great analysis!
– this is mostly StaticArrays.jl?
Yes. Due to Luxor.jl's great age (10 years old this year!) it was never very good at interoperability with other packages - it predates most of the cool and useful ones. Even StaticArrays.jl was a risky and costly dependency back when it was first developed. Recently I've started trying use external packages more - for example, replacing some of the polygon operations with PolygonAlgorithms.jl. And with the new package dependencies/extensions, we can use more external packages without incurring the cost of loading. Perhaps BasicBSplines would work as an extension?
Yes. I've forgotten how they work, now, but I think more Bezier handling is always good. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes can be found in this commit. I don't think I can edit this PR but you can merge the commit to this PR. I can create a new one which I think is not preferred?
@@ -775,6 +775,76 @@ function polyfit(plist::Array{Point,1}, npoints = 30) | |||
return resultpoly | |||
end | |||
|
|||
""" | |||
bspline(controlPoints::Array{Point,1}, npoints; degree=3, clamped=true) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
polybspline
makes sense since we're discussing adding other spline types I guess. Also ugh I yeah you're right for some reason the Julia community hates capital letters lol
src/polygons.jl
Outdated
end | ||
k += 1 | ||
end | ||
println("t: ",t," k: ",k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just forgot to remove that line😅
Maybe... I can try it out and see if it's easily compatible with Luxor. I don't think implementing the basic algorithm will take too long though. Depends on how extensive we want the feature set for the splines and whether you're ok with adding dependencies. |
Yes! There is a plan to replace the dependency with StaticArraysCore.jl, but I haven't finished yet.
I think adding a constructor method julia> C2 # The same definition as in my previous comment
BSplineManifold{1, (3,), Point, Int64, Tuple{BSplineSpace{3, Int64, KnotVector{Int64}}}}(Point[Point(-0.7994182143543495, 0.9390793075737545), Point(-0.22753695196095303, -0.7882913054541987), Point(0.5285175335612737, -0.11298397038382763), Point(0.3865239467411823, 0.27159464933380395), Point(0.3865239467411823, 0.27159464933380395), Point(0.24453035992109085, 0.6561732690514355), Point(-0.7955112992413191, 0.7500231734163277), Point(-0.9177637124041496, 0.8900937347363591), Point(-0.9177637124041496, 0.8900937347363591), Point(-1.0400161255669802, 1.0301642960563906), Point(-0.24447929273023133, 1.2164555143315614), Point(0.1373459430717789, 0.8649713355835442), Point(0.1373459430717789, 0.8649713355835442), Point(0.5191711788737892, 0.513487156835527), Point(0.4872848176410608, -0.37577241893567803), Point(0.309770873250253, -0.5295743517845274), Point(0.309770873250253, -0.5295743517845274), Point(0.13225692885944523, -0.6833762846333767), Point(-0.1908845986894419, -0.10172057455987063), Point(-1.386438336193102, -0.29019694915633065)], (BSplineSpace{3, Int64, KnotVector{Int64}}(KnotVector([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5])),))
julia> path = BezierPath([BezierPathSegment(controlpoints(C2)[1+4i:4+4i]) for i in 0:4]) # This instance will be obtained via the method `BezierPath(::BasicBSpline.BSplineManifold{1, (3,), Point})`.
5-element BezierPath:
[Point(-0.7994182143543495, 0.9390793075737545), Point(-0.22753695196095303, -0.7882913054541987), Point(0.5285175335612737, -0.11298397038382763), Point(0.3865239467411823, 0.27159464933380395)]
[Point(0.3865239467411823, 0.27159464933380395), Point(0.24453035992109085, 0.6561732690514355), Point(-0.7955112992413191, 0.7500231734163277), Point(-0.9177637124041496, 0.8900937347363591)]
[Point(-0.9177637124041496, 0.8900937347363591), Point(-1.0400161255669802, 1.0301642960563906), Point(-0.24447929273023133, 1.2164555143315614), Point(0.1373459430717789, 0.8649713355835442)]
[Point(0.1373459430717789, 0.8649713355835442), Point(0.5191711788737892, 0.513487156835527), Point(0.4872848176410608, -0.37577241893567803), Point(0.309770873250253, -0.5295743517845274)]
[Point(0.309770873250253, -0.5295743517845274), Point(0.13225692885944523, -0.6833762846333767), Point(-0.1908845986894419, -0.10172057455987063), Point(-1.386438336193102, -0.29019694915633065)]
julia> bezierpathtopoly(path; steps=3) # A bezier path can be converted into a vector of points. I'm not sure how to include the last end point. (See `[1:20]` in the next evaluation)
20-element Vector{Point}:
Point(-0.7994182143543495, 0.9390793075737545)
Point(-0.3528844959643831, 0.05196898395480254)
Point(0.06125593464847445, -0.18664398382581515)
Point(0.341544831033803, -0.02926647046663583)
Point(0.3865239467411823, 0.27159464933380395)
Point(0.14001720116102112, 0.5107818139356995)
Point(-0.27302282295295655, 0.6725347139341815)
Point(-0.6823613770061978, 0.7884423529831135)
Point(-0.9177637124041496, 0.8900937347363591)
Point(-0.8581722393237626, 0.99468809056206)
Point(-0.5792380030280007, 1.0618655626854698)
Point(-0.205789207551331, 1.0368765210461206)
Point(0.1373459430717789, 0.8649713355835442)
Point(0.35033275819609594, 0.5204195808298328)
Point(0.43331060073332284, 0.09356764968732056)
Point(0.4114123469843964, -0.2962188603490976)
Point(0.309770873250253, -0.5295743517845274)
Point(0.13797423332130898, -0.5305523328668528)
Point(-0.15656880905410486, -0.396882734815075)
Point(-0.6419943126381785, -0.2697145945909743)
julia> C.(range(domain(bsplinespaces(C)[1]), length=21))[1:20] # The same results as previous one (except for floating point errors)
20-element Vector{Point}:
Point(-0.7994182143543495, 0.9390793075737545)
Point(-0.35288449596438315, 0.051968983954802477)
Point(0.0612559346484744, -0.18664398382581504)
Point(0.34154483103380284, -0.029266470466635504)
Point(0.38652394674118207, 0.2715946493338039)
Point(0.1400172011610212, 0.5107818139356997)
Point(-0.27302282295295666, 0.6725347139341815)
Point(-0.6823613770061979, 0.7884423529831135)
Point(-0.9177637124041496, 0.890093734736359)
Point(-0.8581722393237627, 0.99468809056206)
Point(-0.5792380030280005, 1.0618655626854696)
Point(-0.205789207551331, 1.0368765210461206)
Point(0.1373459430717789, 0.864971335583544)
Point(0.35033275819609594, 0.5204195808298326)
Point(0.4333106007333227, 0.09356764968732044)
Point(0.41141234698439616, -0.29621886034909783)
Point(0.309770873250253, -0.5295743517845274)
Point(0.13797423332130898, -0.5305523328668529)
Point(-0.1565688090541049, -0.3968827348150751)
Point(-0.6419943126381785, -0.26971459459097424)
I'm not sure I understand what you mean. You have already added the commit |
@hyrodium First of all thank you for the feedback! I didn't see your BasicBSpline.jl package when I wrote this code, seems really good! I just wanted to write one myself and then thought I should submit it to Luxor. I am not the most experienced with package load times and pre-compilation so I'm unsure what is the best option for Luxor. I could spend some time to make polybspline run more efficiently to somewhat match that of BasicBSpline.
Ah good, yes that's the one. As I said this my first ever PR I don't have that much experience with them😅 |
Has a decision been made for going with this PR or instead using BasicBSpline.jl? I'm willing to spend some time to get the function up to speed with the package and doing the remainder documentation/package complementary tasks stated in this comment. |
Yes, Bsplines are cool. Happy to have this function added, once the PR is ready to merge. Also happy to have more package extensions linking to specialized packages for other curves - although someone needs to write them first... 😂 I think it's good to have different ways of doing things here; the end result is to put graphics on drawings, so various approaches with different strengths and weaknesses with regard to performance or flexibility (assuming they're mathematically correct) is fine by me. [Contributing to open source occasionally leads to disappointment as well as excitement - I've spent quite a few hours of my life on PRs that went nowhere. You get used to it! 😀] |
Significantly improved the performance of the algorithm (mainly through eliminating a lot of type instabilities). Also added package requirements listed in JuliaGraphics#305 (comment) except for ChangeLog
Ok the PR is now updated with significantly improved performance of the algorithm (mainly through eliminating a lot of type instabilities). Also added package requirements listed in #305 (comment) except for ChangeLog. Following the test made by @hyrodium here
I couldn't get the performance to match that of BasicBSpline which is currently ~150% the speed of polybspline. I've tried all the known suspects for code speed in Julia but I don't I can improve upon it without StaticArrays.jl or the new 1.11 |
@j-adel Thanks for your contribution! |
B-Spline function with clamped/unclamped options using De Boor's Algorithm. It's implemented in a pretty efficient way. However, I'm sure there's plenty of ways to speed it up using vectorization/parallelization.
I think this was the objective of the polyfit() experimental function? I didn't want to replace it so I leave that decision to the maintainer.
But yeah I've been working with B-Splines lately and thought it would be useful to have for this library! I tried to make it similar to the coding style and inputs/outputs of the library functions however any changes in that regard might be a good idea.
I think the only commonly used feature of splines is making them closed (i.e. a loop) but I haven't gotten around to that yet.
The other kind of spline that would be crucial for the library is a Catmull-Rom spline. With several spline types, I was thinking maybe it can be a unified function
curve()
orspline()
which takes as input the spline type, sorta like how Processing does it?Let me know if you think any changes are necessary for the PR. This is my first significant PR actually so I'm guessing I'm missing a lot of stuff lol.