Skip to content

Commit 1687e90

Browse files
committed
implement smooth curve shorthands
1 parent 9bb05e7 commit 1687e90

File tree

4 files changed

+101
-57
lines changed

4 files changed

+101
-57
lines changed

src/Curve.elm

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,12 @@ A nice consequence is that there are no weird bumps in the curve between the dat
9696
-}
9797

9898
import List.Extra as List
99-
import LowLevel.Command as LowLevel exposing (..)
99+
import LowLevel.Command as Command exposing (..)
100100
import SubPath exposing (SubPath(..), close, connect, empty, subpath)
101101
import Vector2 as Vec2 exposing (Vec2)
102102
import Vector3 exposing (Vec3)
103103
import Internal.NaturalInterpolation exposing (naturalControlPoints)
104+
import Path.LowLevel as LowLevel exposing (Mode(Absolute))
104105

105106

106107
epsilon : Float
@@ -154,7 +155,7 @@ linear points =
154155

155156
{-| Shorthand to draw a sequence of cubic bezier segments
156157
-}
157-
cubicBezier : Vec2 Float -> List (Vec3 (Vec2 Float)) -> SubPath
158+
cubicBezier : Vec2 Float -> List ( Vec2 Float, Vec2 Float, Vec2 Float ) -> SubPath
158159
cubicBezier start points =
159160
case points of
160161
[] ->
@@ -166,25 +167,24 @@ cubicBezier start points =
166167

167168
{-| Shorthand to draw a sequence of smooth cubic bezier segments
168169
-}
169-
smoothCubicBezier : Vec2 Float -> Vec3 (Vec2 Float) -> List (Vec2 (Vec2 Float)) -> SubPath
170+
smoothCubicBezier : Vec2 Float -> ( Vec2 Float, Vec2 Float, Vec2 Float ) -> List ( Vec2 Float, Vec2 Float ) -> SubPath
170171
smoothCubicBezier start first points =
171-
Debug.crash "todo "
172-
173-
174-
175-
{-
176-
case points of
177-
[] ->
178-
empty
179-
180-
x :: xs ->
181-
subpath (moveTo start) [ cubicCurveTo [ first ], cubicCurveExtendTo points ]
182-
-}
172+
let
173+
lowLevelDrawTos =
174+
[ LowLevel.CurveTo Absolute [ first ]
175+
, LowLevel.SmoothCurveTo Absolute points
176+
]
177+
178+
lowLevelSubPath : LowLevel.SubPath
179+
lowLevelSubPath =
180+
{ moveto = LowLevel.MoveTo Absolute start, drawtos = lowLevelDrawTos }
181+
in
182+
SubPath.fromLowLevel lowLevelSubPath
183183

184184

185185
{-| Shorthand to draw a sequence of quadratic bezier segments
186186
-}
187-
quadraticBezier : Vec2 Float -> List (Vec2 (Vec2 Float)) -> SubPath
187+
quadraticBezier : Vec2 Float -> List ( Vec2 Float, Vec2 Float ) -> SubPath
188188
quadraticBezier start points =
189189
case points of
190190
[] ->
@@ -196,20 +196,19 @@ quadraticBezier start points =
196196

197197
{-| Shorthand to draw a sequence of smooth quadratic bezier segments
198198
-}
199-
smoothQuadraticBezier : Vec2 Float -> Vec2 (Vec2 Float) -> List (Vec2 Float) -> SubPath
199+
smoothQuadraticBezier : Vec2 Float -> ( Vec2 Float, Vec2 Float ) -> List (Vec2 Float) -> SubPath
200200
smoothQuadraticBezier start first points =
201-
Debug.crash "todo"
202-
203-
204-
205-
{-
206-
case points of
207-
[] ->
208-
empty
209-
210-
x :: xs ->
211-
subpath (moveTo start) [ quadraticCurveTo [ first ], quadraticCurveExtendTo points ]
212-
-}
201+
let
202+
lowLevelDrawTos =
203+
[ LowLevel.QuadraticBezierCurveTo Absolute [ first ]
204+
, LowLevel.SmoothQuadraticBezierCurveTo Absolute points
205+
]
206+
207+
lowLevelSubPath : LowLevel.SubPath
208+
lowLevelSubPath =
209+
{ moveto = LowLevel.MoveTo Absolute start, drawtos = lowLevelDrawTos }
210+
in
211+
SubPath.fromLowLevel lowLevelSubPath
213212

214213

215214
{-| Draw a straigt line between the data points, connecting the ends.

src/Path.elm

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ module Path
44
, element
55
, parse
66
, toString
7+
, fromLowLevel
8+
, toLowLevel
79
)
810

911
{-| Module for layering SubPaths into Paths.
@@ -27,11 +29,17 @@ Most of the interesting stuff happens in the `SubPath` and `Curve` modules.
2729
2830
@docs element, toString
2931
32+
## Conversion
33+
34+
@docs fromLowLevel, toLowLevel
35+
3036
-}
3137

3238
import Parser
3339
import Path.LowLevel.Parser as PathParser
40+
import Path.LowLevel as LowLevel
3441
import SubPath exposing (SubPath, subpath)
42+
import LowLevel.Command as Command
3543
import Svg
3644
import Svg.Attributes
3745

@@ -96,4 +104,42 @@ The error type is [`Parser.Error`](http://package.elm-lang.org/packages/elm-tool
96104
-}
97105
parse : String -> Result Parser.Error Path
98106
parse =
99-
Result.map SubPath.fromLowLevel << PathParser.parse
107+
Result.map fromLowLevel << PathParser.parse
108+
109+
110+
{-| Converting a svg-path-lowlevel subpath into a one-true-path subpath. Used in parsing
111+
-}
112+
fromLowLevel : List LowLevel.SubPath -> Path
113+
fromLowLevel lowlevels =
114+
case lowlevels of
115+
[] ->
116+
[]
117+
118+
first :: _ ->
119+
-- first moveto is always interpreted absolute
120+
case first.moveto of
121+
LowLevel.MoveTo _ target ->
122+
let
123+
initialCursorState =
124+
{ start = target, cursor = target, previousControlPoint = Nothing }
125+
126+
folder { moveto, drawtos } ( state, accum ) =
127+
let
128+
( stateAfterMoveTo, newMoveTo ) =
129+
Command.fromLowLevelMoveTo moveto state
130+
131+
( stateAfterDrawtos, newDrawTos ) =
132+
Command.fromLowLevelDrawTos drawtos stateAfterMoveTo
133+
in
134+
( stateAfterDrawtos, subpath newMoveTo newDrawTos :: accum )
135+
in
136+
List.foldl folder ( initialCursorState, [] ) lowlevels
137+
|> Tuple.second
138+
|> List.reverse
139+
140+
141+
{-| Convert a path to a svg-path-lowlevel list of subpaths
142+
-}
143+
toLowLevel : Path -> List LowLevel.SubPath
144+
toLowLevel =
145+
List.filterMap SubPath.toLowLevel

src/SubPath.elm

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -518,34 +518,19 @@ scale vec subpath =
518518

519519

520520
{-| Converting a svg-path-lowlevel subpath into a one-true-path subpath. Used in parsing
521-
-}
522-
fromLowLevel : List LowLevel.SubPath -> List SubPath
523-
fromLowLevel lowlevels =
524-
case lowlevels of
525-
[] ->
526-
[]
527-
528-
first :: _ ->
529-
-- first moveto is always interpreted absolute
530-
case first.moveto of
531-
LowLevel.MoveTo _ target ->
532-
let
533-
initialCursorState =
534-
{ start = target, cursor = target, previousControlPoint = Nothing }
535-
536-
folder { moveto, drawtos } ( state, accum ) =
537-
let
538-
( stateAfterMoveTo, newMoveTo ) =
539-
Command.fromLowLevelMoveTo moveto state
540521
541-
( stateAfterDrawtos, newDrawTos ) =
542-
Command.fromLowLevelDrawTos drawtos stateAfterMoveTo
543-
in
544-
( stateAfterDrawtos, SubPath { moveto = newMoveTo, drawtos = Deque.fromList newDrawTos } :: accum )
545-
in
546-
List.foldl folder ( initialCursorState, [] ) lowlevels
547-
|> Tuple.second
548-
|> List.reverse
522+
> Beware that the moveto is always interpreted as **Absolute**.
523+
-}
524+
fromLowLevel : LowLevel.SubPath -> SubPath
525+
fromLowLevel { moveto, drawtos } =
526+
-- first moveto is always interpreted absolute
527+
case moveto of
528+
LowLevel.MoveTo _ target ->
529+
let
530+
initialCursorState =
531+
{ start = target, cursor = target, previousControlPoint = Nothing }
532+
in
533+
subpath (MoveTo target) (Tuple.second <| Command.fromLowLevelDrawTos drawtos initialCursorState)
549534

550535

551536
{-| Converting a one-true-path subpath into a svg-path-lowlevel subpath. Used in toString

tests/PathTest.elm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,20 @@ various =
220220
( { startConfig | cursor = newCursor, start = newStart, previousControlPoint = Nothing }
221221
, MoveTo newCursor
222222
)
223+
, test "parsing and conversion of smooth quadratic is correct" <|
224+
\_ ->
225+
"M10 80 Q 52.5 10, 95 80 T 180 80"
226+
|> Path.parse
227+
|> Result.withDefault []
228+
|> Path.toString
229+
|> Expect.equal "M10,80 Q52.5,10 95,80 Q137.5,150 180,80"
230+
, test "parsing and conversion of smooth cubic is correct" <|
231+
\_ ->
232+
"M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80"
233+
|> Path.parse
234+
|> Result.withDefault []
235+
|> Path.toString
236+
|> Expect.equal "M10,80 C40,10 65,10 95,80 C125,150 150,150 180,80"
223237
]
224238

225239

0 commit comments

Comments
 (0)