<commit_before>module QuadraticSpline2d
    exposing
        ( jsonRoundTrips
        , parameterization
        )

import Expect
import Generic
import OpenSolid.Geometry.Decode as Decode
import OpenSolid.Geometry.Encode as Encode
import OpenSolid.Geometry.Fuzz as Fuzz
import OpenSolid.Point2d as Point2d
import OpenSolid.QuadraticSpline2d as QuadraticSpline2d exposing (QuadraticSpline2d)
import Test exposing (Test)


exampleSpline : QuadraticSpline2d
exampleSpline =
    QuadraticSpline2d.fromControlPoints
        ( Point2d.fromCoordinates ( 1, 1 )
        , Point2d.fromCoordinates ( 3, 4 )
        , Point2d.fromCoordinates ( 5, 1 )
        )


line : QuadraticSpline2d
line =
    QuadraticSpline2d.fromControlPoints
        ( Point2d.fromCoordinates ( 0, 1 )
        , Point2d.fromCoordinates ( 2.5, 1 )
        , Point2d.fromCoordinates ( 5, 1 )
        )


parameterization : Test
parameterization =
    Test.describe "arc length parameterization"
        [ Test.test "at s=0 the result is the starting point of the spline" <|
            \_ ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001
                            exampleSpline
                in
                QuadraticSpline2d.pointAtLength parameterization 0
                    |> Expect.equal (Just (Point2d.fromCoordinates ( 1, 1 )))
        , Test.fuzz Fuzz.quadraticSpline2d "at s = 0 the arcLengthParameterization gives the starting point" <|
            \spline ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001
                            spline

                    startPoint =
                        QuadraticSpline2d.startPoint spline
                in
                QuadraticSpline2d.pointAtLength parameterization 0
                    |> Expect.equal (Just startPoint)
        , Test.fuzz Fuzz.quadraticSpline2d "at s = (length spline) the arcLengthParameterization gives the end point" <|
            \spline ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001 spline

                    endPoint =
                        QuadraticSpline2d.endPoint spline
                in
                QuadraticSpline2d.arcLength parameterization
                    |> QuadraticSpline2d.pointAtLength parameterization
                    |> Expect.equal (Just endPoint)
        , Test.fuzz Fuzz.quadraticSpline2d "at s = (length spline) parameterValueAtLength gives 1" <|
            \spline ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001 spline
                in
                QuadraticSpline2d.arcLength parameterization
                    |> QuadraticSpline2d.parameterValueAtLength parameterization
                    |> Expect.equal (Just 1)
        , Test.test "at s=5 the result is the end point of the line" <|
            \_ ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.1 line
                in
                QuadraticSpline2d.pointAtLength parameterization 5
                    |> Expect.equal (Just (Point2d.fromCoordinates ( 5, 1 )))
        , Test.test "at s=2.5 the result is the midpoint of the line" <|
            \_ ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.1 line
                in
                QuadraticSpline2d.pointAtLength parameterization 2.5
                    |> Expect.equal (Just (Point2d.fromCoordinates ( 2.5, 1 )))
        , Test.test "at s=2.5 is about right" <|
            \_ ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001<commit_msg>Add test to verify arc length is computed correctly<commit_after>module QuadraticSpline2d
    exposing
        ( jsonRoundTrips
        , parameterization
        )

import Expect
import Fuzz exposing (Fuzzer)
import Generic
import OpenSolid.Geometry.Decode as Decode
import OpenSolid.Geometry.Encode as Encode
import OpenSolid.Geometry.Fuzz as Fuzz
import OpenSolid.Point2d as Point2d
import OpenSolid.QuadraticSpline2d as QuadraticSpline2d exposing (QuadraticSpline2d)
import OpenSolid.Scalar as Scalar
import Test exposing (Test)


curvedSpline : Fuzzer QuadraticSpline2d
curvedSpline =
    Fuzz.map5
        (\angle length interpolationParameter offset flipSide ->
            let
                x0 =
                    -length / 2

                x2 =
                    length / 2

                x1 =
                    Scalar.interpolateFrom x0 x2 interpolationParameter

                y1 =
                    if flipSide then
                        -offset
                    else
                        offset

                p0 =
                    Point2d.fromCoordinates ( x0, 0 )

                p1 =
                    Point2d.fromCoordinates ( x1, y1 )

                p2 =
                    Point2d.fromCoordinates ( x2, 0 )
            in
            QuadraticSpline2d.fromControlPoints ( p0, p1, p2 )
                |> QuadraticSpline2d.rotateAround Point2d.origin angle
        )
        (Fuzz.floatRange 0 (2 * pi))
        (Fuzz.floatRange 1 10)
        (Fuzz.floatRange 0 1)
        (Fuzz.floatRange 1 5)
        Fuzz.bool


analyticalLength : QuadraticSpline2d -> Float
analyticalLength spline =
    let
        ( p0, p1, p2 ) =
            QuadraticSpline2d.controlPoints spline

        ( x0, y0 ) =
            Point2d.coordinates p0

        ( x1, y1 ) =
            Point2d.coordinates p1

        ( x2, y2 ) =
            Point2d.coordinates p2

        ax =
            x0 - 2 * x1 + x2

        ay =
            y0 - 2 * y1 + y2

        bx =
            2 * x1 - 2 * x0

        by =
            2 * y1 - 2 * y0

        a =
            4 * (ax * ax + ay * ay)

        b =
            4 * (ax * bx + ay * by)

        c =
            bx * bx + by * by

        s_abc =
            2 * sqrt (a + b + c)

        a_2 =
            sqrt a

        a_32 =
            2 * a * a_2

        c_2 =
            2 * sqrt c

        ba =
            b / a_2
    in
    (a_32 * s_abc + a_2 * b * (s_abc - c_2) + (4 * c * a - b * b) * logBase e ((2 * a_2 + ba + s_abc) / (ba + c_2))) / (4 * a_32)


exampleSpline : QuadraticSpline2d
exampleSpline =
    QuadraticSpline2d.fromControlPoints
        ( Point2d.fromCoordinates ( 1, 1 )
        , Point2d.fromCoordinates ( 3, 4 )
        , Point2d.fromCoordinates ( 5, 1 )
        )


line : QuadraticSpline2d
line =
    QuadraticSpline2d.fromControlPoints
        ( Point2d.fromCoordinates ( 0, 1 )
        , Point2d.fromCoordinates ( 2.5, 1 )
        , Point2d.fromCoordinates ( 5, 1 )
        )


parameterization : Test
parameterization =
    Test.describe "arc length parameterization"
        [ Test.test "at s=0 the result is the starting point of the spline" <|
            \_ ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001
                            exampleSpline
                in
                QuadraticSpline2d.pointAtLength parameterization 0
                    |> Expect.equal (Just (Point2d.fromCoordinates ( 1, 1 )))
        , Test.fuzz Fuzz.quadraticSpline2d "at s = 0 the arcLengthParameterization gives the starting point" <|
            \spline ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001
                            spline

                    startPoint =
                        QuadraticSpline2d.startPoint spline
                in
                QuadraticSpline2d.pointAtLength parameterization 0
                    |> Expect.equal (Just startPoint)
        , Test.fuzz Fuzz.quadraticSpline2d "at s = (length spline) the arcLengthParameterization gives the end point" <|
            \spline ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001 spline

                    endPoint =
                        QuadraticSpline2d.endPoint spline
                in
                QuadraticSpline2d.arcLength parameterization
                    |> QuadraticSpline2d.pointAtLength parameterization
                    |> Expect.equal (Just endPoint)
        , Test.fuzz Fuzz.quadraticSpline2d "at s = (length spline) parameterValueAtLength gives 1" <|
            \spline ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001 spline
                in
                QuadraticSpline2d.arcLength parameterization
                    |> QuadraticSpline2d.parameterValueAtLength parameterization
                    |> Expect.equal (Just 1)
        , Test.fuzz curvedSpline "arc length matches analytical formula" <|
            -- analyticalLength falls down for degenerate splines so just check
            -- curved ones
            \spline ->
                let
                    tolerance =
                        0.001

                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization tolerance
                            spline
                in
                QuadraticSpline2d.arcLength parameterization
                    |> Expect.within (Expect.Absolute tolerance)
                        (analyticalLength spline)
        , Test.test "at s=5 the result is the end point of the line" <|
            \_ ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.1 line
                in
                QuadraticSpline2d.pointAtLength parameterization 5
                    |> Expect.equal (Just (Point2d.fromCoordinates ( 5, 1 )))
        , Test.test "at s=2.5 the result is the midpoint of the line" <|
            \_ ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.1 line
                in
                QuadraticSpline2d.pointAtLength parameterization 2.5
                    |> Expect.equal (Just (Point2d.fromCoordinates ( 2.5, 1 )))
        , Test.test "at s=2.5 is about right" <|
            \_ ->
                let
                    parameterization =
                        QuadraticSpline2d.arcLengthParameterization 0.001
{'fraction_non_alphanumeric': 0.06048990276738968, 'fraction_numerical': 0.027299925205684368, 'mean_word_length': 1.3603265666372462, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5803104', 'n_tokens_mistral': 3185, 'n_tokens_neox': 2795, 'n_words': 682}