Skip to content
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

Addition of Bezier curves #289

Closed
bvenn opened this issue Aug 10, 2023 · 1 comment · Fixed by #305
Closed

Addition of Bezier curves #289

bvenn opened this issue Aug 10, 2023 · 1 comment · Fixed by #305
Labels
Difficulty: Advanced Hackathon projects with beginner difficulty Difficulty: Intermediate Hackathon projects with intermediate difficulty FsLab Hackathon 2023 Implementation projects for the 2023 FsLab Hackathon

Comments

@bvenn
Copy link
Member

bvenn commented Aug 10, 2023

Description

Bezier curves can be found in sketching software, optimization algorithms and game/movie animations. A curve is drawn between two points that is controlled by m ={1,2,3 .. } control points. If m=1 the resulting curve is quadratic, m=2 results in cubic curves etc.. Nested LERPs are used to identify the desired y-value from an given x value.

Quadratic:

image

Cubic:

image

Fourth-order curve:

image

De Casteljau's algorithm allows the efficient calculation of these curves.

Pointers

  • The implementation should take collections of x and y values and may return a function that takes an x value of which the corresponding y value should be predicted.
  • Note that the first and last x,y tuple are the points that are interpolated.
  • The degree of the resulting curve can be easily inferred from the number of x,y coordinates present in the input data
    • if xValues.Length = 4, the first and last point mark the start and end of the curve, while the internal two points serve as control points, leading the resulting curve to be cubic (n-1).
  • Attention: In computer graphics the x coordinate is often used as t, that describes the percentage of traversal. You can add two separate functions, that either takes x (bezier) or t (bezierFromPercentage).
  • let bezier (xValues: float [] <or any other appropriate collection type>) (yValues: float []): (float -> float) =
        fun x -> 
           // iteratively LERP the points until you end up with the final y coordinate for the given x coordinate
  • There are several pseudocode examples and implementations that you can use as coding hint!
  • I think the bezier function would best fit here (but of course you can first develop in notebooks and we assist in in coorperate it into FSharp.Stats):
  • Note the following example works, but is limited to cubic bezier curves. If you want to use it, you have to generalize it to work on collections of any length (recursion).
#r "nuget: Plotly.NET.Interactive, 4.2.1"
#r "nuget: FSharp.Stats, 0.5.0"

open Plotly.NET
open Plotly.NET.StyleParam
open FSharp.Stats

let t = 0.3
let p0 = vector [|1.;1.|] //point 0 that should be traversed
let c0 = vector [|1.1;2.1|] //control point 0
let c1 = vector [|3.8;1.6|] //control point 1
let p1 = vector [|3.;2.|] //point 1 that should be traversed

let lerp (p1: vector) (p2: vector) (t: float) = 
    p1 + t * (p2 - p1)

let a = lerp p0 c0 t
let b = lerp c0 c1 t
let c = lerp c1 p1 t
let d = lerp a b t
let e = lerp b c t
let f = lerp d e t

let assemble t = 
    let a = lerp p0 c0 t
    let b = lerp c0 c1 t
    let c = lerp c1 p1 t
    let d = lerp a b t
    let e = lerp b c t
    let f = lerp d e t
    f.[0],f.[1]

let chart = 
    [
        Chart.Point([p0.[0]],[p0.[1]],Name="Point_0",MarkerColor=Color.fromHex "#1f77b4") |> Chart.withMarkerStyle(Size=12)
        Chart.Point([c0.[0]],[c0.[1]],Name="Control_0",MarkerColor=Color.fromHex "#ff7f0e")|> Chart.withMarkerStyle(Size=10)
        Chart.Point([c1.[0]],[c1.[1]],Name="Control_1",MarkerColor=Color.fromHex "#ff7f0e")|> Chart.withMarkerStyle(Size=10)
        Chart.Point([p1.[0]],[p1.[1]],Name="Point_1",MarkerColor=Color.fromHex "#1f77b4") |> Chart.withMarkerStyle(Size=12)

        Chart.Line([p0.[0];c0.[0]],[p0.[1];c0.[1]],Name="LERP_A",MarkerColor=Color.fromHex "#919191")
        Chart.Line([c0.[0];c1.[0]],[c0.[1];c1.[1]],Name="LERP_B",MarkerColor=Color.fromHex "#919191")
        Chart.Line([c1.[0];p1.[0]],[c1.[1];p1.[1]],Name="LERP_C",MarkerColor=Color.fromHex "#919191")

        Chart.Point([a.[0]],[a.[1]],Name="A_t",MarkerColor=Color.fromHex "#000000")
        Chart.Point([b.[0]],[b.[1]],Name="B_t",MarkerColor=Color.fromHex "#000000")
        Chart.Point([c.[0]],[c.[1]],Name="C_t",MarkerColor=Color.fromHex "#000000")

        Chart.Line([a.[0];b.[0]],[a.[1];b.[1]],Name="LERP_D",MarkerColor=Color.fromHex "#5d5d5d")
        Chart.Line([b.[0];c.[0]],[b.[1];c.[1]],Name="LERP_E",MarkerColor=Color.fromHex "#5d5d5d")
        
        Chart.Point([d.[0]],[d.[1]],Name="D_t",MarkerColor=Color.fromHex "#000000")
        Chart.Point([e.[0]],[e.[1]],Name="E_t",MarkerColor=Color.fromHex "#000000")

        Chart.Line([d.[0];e.[0]],[d.[1];e.[1]],Name="LERP_F",MarkerColor=Color.fromHex "#292929")

        Chart.Point([f.[0]],[f.[1]],Name="F_t",MarkerColor=Color.fromHex "#1f77b4") |> Chart.withMarkerStyle(Size=12)

        [0. .. 0.01 .. 0.3] |> List.map (fun t -> assemble t) |> Chart.Line |> Chart.withTraceInfo "Bezier" |> Chart.withLineStyle(Color=Color.fromHex "#1f77b4",Width=4.)
    ]
    |> Chart.combine
    |> Chart.withTemplate ChartTemplates.lightMirrored
    |> Chart.show

image

References

Hints (click to expand if you need additional pointers)
let t = 0.3
let p0 = vector [|1.;1.;1.|] //point 0 that should be traversed
let c0 = vector [|1.1;2.1;2.|] //control point 0
let c1 = vector [|3.8;1.6;1.4|] //control point 1
let p1 = vector [|3.;2.;0.|] //point 1 that should be traversed

let lerp (p1: vector) (p2: vector) (t: float) = 
    p1 + t * (p2 - p1)

let a = lerp p0 c0 t
let b = lerp c0 c1 t
let c = lerp c1 p1 t

let d = lerp a b t
let e = lerp b c t

let f = lerp d e t

let assemble t = 
    let a = lerp p0 c0 t
    let b = lerp c0 c1 t
    let c = lerp c1 p1 t
    let d = lerp a b t
    let e = lerp b c t
    let f = lerp d e t
    f.[0],f.[1],f.[2]

let chart = 
    [
        Chart.Point3D([p0.[0]],[p0.[1]],[p0.[2]],Name="Point_0",MarkerColor=Color.fromHex "#1f77b4") |> Chart.withMarkerStyle(Size=12)
        Chart.Point3D([c0.[0]],[c0.[1]],[c0.[2]],Name="Control_0",MarkerColor=Color.fromHex "#ff7f0e")|> Chart.withMarkerStyle(Size=10)
        Chart.Point3D([c1.[0]],[c1.[1]],[c1.[2]],Name="Control_1",MarkerColor=Color.fromHex "#ff7f0e")|> Chart.withMarkerStyle(Size=10)
        Chart.Point3D([p1.[0]],[p1.[1]],[p1.[2]],Name="Point_1",MarkerColor=Color.fromHex "#1f77b4") |> Chart.withMarkerStyle(Size=12)

        Chart.Line3D([p0.[0],p0.[1],p0.[2];c0.[0],c0.[1],c0.[2]],Name="LERP_A",MarkerColor=Color.fromHex "#919191")
        Chart.Line3D([c0.[0],c0.[1],c0.[2];c1.[0],c1.[1],c1.[2]],Name="LERP_B",MarkerColor=Color.fromHex "#919191")
        Chart.Line3D([c1.[0],c1.[1],c1.[2];p0.[1],p1.[1],p1.[2]],Name="LERP_C",MarkerColor=Color.fromHex "#919191")

        Chart.Point3D([a.[0]],[a.[1]],[a.[2]],Name="A_t",MarkerColor=Color.fromHex "#000000")
        Chart.Point3D([b.[0]],[b.[1]],[b.[2]],Name="B_t",MarkerColor=Color.fromHex "#000000")
        Chart.Point3D([c.[0]],[c.[1]],[c.[2]],Name="C_t",MarkerColor=Color.fromHex "#000000")

        Chart.Line3D([a.[0],a.[1],a.[2];b.[0],b.[1],b.[2]],Name="LERP_D",MarkerColor=Color.fromHex "#5d5d5d")
        Chart.Line3D([b.[0],b.[1],b.[2];c.[0],c.[1],c.[2]],Name="LERP_E",MarkerColor=Color.fromHex "#5d5d5d")
        
        Chart.Point3D([d.[0]],[d.[1]],[d.[2]],Name="D_t",MarkerColor=Color.fromHex "#000000")
        Chart.Point3D([e.[0]],[e.[1]],[e.[2]],Name="E_t",MarkerColor=Color.fromHex "#000000")

        Chart.Line3D([d.[0],d.[1],d.[2];e.[0],e.[1],e.[2]],Name="LERP_F",MarkerColor=Color.fromHex "#292929")

        Chart.Point3D([f.[0]],[f.[1]],[f.[2]],Name="F_t",MarkerColor=Color.fromHex "#1f77b4") |> Chart.withMarkerStyle(Size=12)

        [0. .. 0.01 .. 0.3] |> List.map (fun t -> assemble t) |> Chart.Line3D |> Chart.withTraceInfo "Bezier" |> Chart.withLineStyle(Color=Color.fromHex "#1f77b4",Width=10.)
    ]
    |> Chart.combine
    |> Chart.withTemplate ChartTemplates.lightMirrored
    |> Chart.show
@bvenn bvenn changed the title [Feature Request] Addition of Bezier curves Addition of Bezier curves Sep 21, 2023
@bvenn bvenn added Difficulty: Advanced Hackathon projects with beginner difficulty FsLab Hackathon 2023 Implementation projects for the 2023 FsLab Hackathon Status: Available Difficulty: Intermediate Hackathon projects with intermediate difficulty labels Sep 21, 2023
@HarryMcCarney HarryMcCarney changed the title Addition of Bezier curves Addition of Bezier curvesfddf Sep 22, 2023
@HarryMcCarney HarryMcCarney changed the title Addition of Bezier curvesfddf Addition of Bezier curves Sep 22, 2023
@pirrmann
Copy link
Contributor

I'm interested by this issue, is someone else going to work on that today?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Advanced Hackathon projects with beginner difficulty Difficulty: Intermediate Hackathon projects with intermediate difficulty FsLab Hackathon 2023 Implementation projects for the 2023 FsLab Hackathon
Projects
Status: Status: Completed
Development

Successfully merging a pull request may close this issue.

2 participants