Skip to content

Instantly share code, notes, and snippets.

@folkertdev
Created August 5, 2017 15:11
Show Gist options
  • Save folkertdev/ff53e9b1182d2fc29e937e69dfe5c50d to your computer and use it in GitHub Desktop.
Save folkertdev/ff53e9b1182d2fc29e937e69dfe5c50d to your computer and use it in GitHub Desktop.
A proposal to allow easier composition of SubPaths
module SubPath exposing (..)
import Path exposing (DrawTo(..), MoveTo(..), lineTo, closePath, moveTo)
import Deque exposing (Deque)
import Vector2 as Vec2 exposing (Vec2)
import List.Extra as List
type SubPath
= SubPath { moveto : MoveTo, drawtos : Deque DrawTo }
| Empty
subpath : MoveTo -> List DrawTo -> SubPath
subpath moveto drawtos =
SubPath { moveto = moveto, drawtos = Deque.fromList drawtos }
area : List ( ( Float, Float ), ( Float, Float ) ) -> SubPath
area points =
let
( low, high ) =
points
|> List.unzip
in
step 1.0 low
|> connect (step 1.0 high)
|> close
{-| Join two subpaths, connecting them with a straight line
-}
connect : SubPath -> SubPath -> SubPath
connect x y =
case ( x, y ) of
( Empty, Empty ) ->
Empty
( Empty, subpath ) ->
subpath
( subpath, Empty ) ->
subpath
( SubPath a, SubPath b ) ->
let
(MoveTo secondStart) =
b.moveto
in
SubPath
{ moveto = a.moveto
, drawtos =
Deque.append a.drawtos (Deque.pushFront (lineTo [ secondStart ]) b.drawtos)
}
close : SubPath -> SubPath
close subpath =
case subpath of
Empty ->
Empty
SubPath { moveto, drawtos } ->
case Deque.popBack drawtos of
( Just ClosePath, preceding ) ->
-- subpath is already closed, return original
subpath
_ ->
SubPath { moveto = moveto, drawtos = Deque.pushBack closePath drawtos }
{-| Draw straight lines between the data points.
<img style="max-width: 100%;" src="https://rawgit.com/folkertdev/one-true-path-experiment/master/docs/linear.svg" />
-}
linear : List (Vec2 Float) -> SubPath
linear points =
case points of
[] ->
Empty
x :: xs ->
subpath (moveTo x) [ lineTo xs ]
{-| Step goes some distance to the right, then to the y-coordinate of the next data point, and then draws to the next point.
The first argument determines where the step is.
* `step 1 points` is `stepAfter`
* `step 0 points` is `stepBefore`
* `step 0.5 points` steps exactly in the middle
<img style="max-width: 100%;" src="https://rawgit.com/folkertdev/one-true-path-experiment/master/docs/step.svg" />
-}
step : Float -> List (Vec2 Float) -> SubPath
step factor points =
let
helper ( x0, y0 ) ( x, y ) =
if factor <= 0 then
[ ( x0, y ), ( x, y ) ]
else
let
x1 =
x0 * (1 - factor) + x * factor
in
[ ( x1, y0 ), ( x1, y ) ]
in
case points of
[] ->
Empty
p :: ps ->
p
:: (List.concat (List.map2 helper points ps) ++ [ List.last ps |> Maybe.withDefault p ])
|> linear
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment