Win a fight with svg transforms
This commit is contained in:
parent
44ed1900d9
commit
929c8cedfb
|
@ -562,7 +562,7 @@ workspace:
|
||||||
extra_packages:
|
extra_packages:
|
||||||
debugged:
|
debugged:
|
||||||
git: https://github.com/mateiadrielrafael/purescript-debugged.git
|
git: https://github.com/mateiadrielrafael/purescript-debugged.git
|
||||||
ref: 0d5a4149279129f10c8fe2a3ef280b9fde4d5116
|
ref: 70d4b2e19c8831753a62a4527b0c8c40a25b2a4e
|
||||||
packages:
|
packages:
|
||||||
aff:
|
aff:
|
||||||
type: registry
|
type: registry
|
||||||
|
@ -694,7 +694,7 @@ packages:
|
||||||
debugged:
|
debugged:
|
||||||
type: git
|
type: git
|
||||||
url: https://github.com/mateiadrielrafael/purescript-debugged.git
|
url: https://github.com/mateiadrielrafael/purescript-debugged.git
|
||||||
rev: 0d5a4149279129f10c8fe2a3ef280b9fde4d5116
|
rev: 70d4b2e19c8831753a62a4527b0c8c40a25b2a4e
|
||||||
dependencies:
|
dependencies:
|
||||||
- arrays
|
- arrays
|
||||||
- bifunctors
|
- bifunctors
|
||||||
|
|
|
@ -23,4 +23,5 @@ workspace:
|
||||||
extra_packages:
|
extra_packages:
|
||||||
debugged:
|
debugged:
|
||||||
git: https://github.com/mateiadrielrafael/purescript-debugged.git
|
git: https://github.com/mateiadrielrafael/purescript-debugged.git
|
||||||
ref: 0d5a4149279129f10c8fe2a3ef280b9fde4d5116
|
ref: 70d4b2e19c8831753a62a4527b0c8c40a25b2a4e
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ type PhysicalExecutionStep = { block :: Array PhysicalKey, keys :: Array Physica
|
||||||
transformKey :: ScalePreservingTransform -> PhysicalKey -> PhysicalKey
|
transformKey :: ScalePreservingTransform -> PhysicalKey -> PhysicalKey
|
||||||
transformKey transform (PhysicalKey key) = PhysicalKey
|
transformKey transform (PhysicalKey key) = PhysicalKey
|
||||||
{ size: key.size
|
{ size: key.size
|
||||||
, transform: composeScalePreservingTransforms key.transform transform
|
, transform: composeScalePreservingTransforms transform key.transform
|
||||||
}
|
}
|
||||||
|
|
||||||
buildPhysical :: RawPhysical -> PhysicalLayout
|
buildPhysical :: RawPhysical -> PhysicalLayout
|
||||||
|
|
|
@ -3,19 +3,21 @@ module LayoutLens.Data.Geometry where
|
||||||
import LayoutLens.Prelude
|
import LayoutLens.Prelude
|
||||||
|
|
||||||
import Data.Array as Array
|
import Data.Array as Array
|
||||||
|
import LayoutLens.Data.Vec2 as V
|
||||||
import LayoutLens.Data.Vec2
|
import LayoutLens.Data.Vec2
|
||||||
( AABB(..)
|
( AABB(..)
|
||||||
, Polygon(..)
|
, Polygon(..)
|
||||||
, ScalePreservingTransform
|
|
||||||
, Vec2(..)
|
, Vec2(..)
|
||||||
, aabbToPolygon
|
, aabbToPolygon
|
||||||
, applyScalePreservingTransform
|
, applyTransform
|
||||||
|
, boundingBox
|
||||||
, mapPoints
|
, mapPoints
|
||||||
|
, tTranslate
|
||||||
, vinv
|
, vinv
|
||||||
, vscale
|
, vscale
|
||||||
)
|
)
|
||||||
|
|
||||||
data Attribute = Fill Color | Stroke Color
|
data Attribute = Fill Color | Stroke Color | StrokeWidth Number
|
||||||
type Attributes = Array Attribute
|
type Attributes = Array Attribute
|
||||||
type GenericAttributes = Array (String /\ String)
|
type GenericAttributes = Array (String /\ String)
|
||||||
|
|
||||||
|
@ -31,28 +33,30 @@ data PathStep
|
||||||
| Close
|
| Close
|
||||||
|
|
||||||
data Geometry
|
data Geometry
|
||||||
= Transform ScalePreservingTransform Geometry
|
= Transform V.Transform Geometry
|
||||||
| Many (Array Geometry)
|
| Many (Array Geometry)
|
||||||
| Text GenericAttributes Attributes String
|
| Text GenericAttributes Attributes String
|
||||||
| Rect AABB Attributes
|
| Rect AABB Attributes
|
||||||
| Path (Array PathStep) Attributes
|
| Path (Array PathStep) Attributes
|
||||||
|
| Invisible Geometry
|
||||||
|
|
||||||
-- Approximate the size of some geometry by fitting a polygon around it
|
-- Approximate the size of some geometry by fitting a polygon around it
|
||||||
boundingPolygon :: Geometry -> Polygon
|
boundingPolygon :: Geometry -> Maybe Polygon
|
||||||
boundingPolygon = case _ of
|
boundingPolygon = case _ of
|
||||||
Rect aabb _ -> aabbToPolygon aabb
|
Rect aabb _ -> pure $ aabbToPolygon aabb
|
||||||
Text _ _ _ -> mempty
|
Text _ _ _ -> Nothing
|
||||||
Many array -> foldMap boundingPolygon array
|
Many array -> foldMap boundingPolygon array
|
||||||
Transform t g -> mapPoints (applyScalePreservingTransform t) $ boundingPolygon g
|
Invisible g -> boundingPolygon g
|
||||||
|
Transform t g -> mapPoints (applyTransform t) <$> boundingPolygon g
|
||||||
Path steps _ -> foldMap snd $ Array.scanl (points <<< fst) mempty steps
|
Path steps _ -> foldMap snd $ Array.scanl (points <<< fst) mempty steps
|
||||||
where
|
where
|
||||||
points :: Vec2 -> PathStep -> Vec2 /\ Polygon
|
points :: Vec2 -> PathStep -> Vec2 /\ Maybe Polygon
|
||||||
points prev = case _ of
|
points prev = case _ of
|
||||||
Close -> prev /\ Polygon []
|
Close -> prev /\ Nothing
|
||||||
MoveTo a -> a /\ Polygon [ a ]
|
MoveTo a -> a /\ (pure $ Polygon $ pure a)
|
||||||
LineTo a -> a /\ Polygon [ a ]
|
LineTo a -> a /\ (pure $ Polygon $ pure a)
|
||||||
-- This is just an approximation where we fit an AABB around the circle.
|
-- This is just an approximation where we fit an AABB around the circle.
|
||||||
Arc arc -> arc.to /\ aabbToPolygon aabb
|
Arc arc -> arc.to /\ pure (aabbToPolygon aabb)
|
||||||
where
|
where
|
||||||
aabb = AABB
|
aabb = AABB
|
||||||
{ position: center <> vinv diagonal
|
{ position: center <> vinv diagonal
|
||||||
|
@ -62,6 +66,21 @@ boundingPolygon = case _ of
|
||||||
diagonal = Vec2 arc.radius arc.radius
|
diagonal = Vec2 arc.radius arc.radius
|
||||||
center = vscale 0.5 $ arc.to <> prev
|
center = vscale 0.5 $ arc.to <> prev
|
||||||
|
|
||||||
|
-- | Add padding around some geometry
|
||||||
|
pad :: Vec2 -> Geometry -> Geometry
|
||||||
|
pad padding geometry = case boundingBox <$> boundingPolygon geometry of
|
||||||
|
Just (AABB box) -> Many
|
||||||
|
[ Transform (tTranslate padding) geometry
|
||||||
|
, Invisible $ Rect
|
||||||
|
( AABB
|
||||||
|
{ position: box.position
|
||||||
|
, size: box.size <> vscale 2.0 padding
|
||||||
|
}
|
||||||
|
)
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
Nothing -> geometry
|
||||||
|
|
||||||
derive instance Eq Attribute
|
derive instance Eq Attribute
|
||||||
derive instance Eq PathStep
|
derive instance Eq PathStep
|
||||||
derive instance Eq Geometry
|
derive instance Eq Geometry
|
||||||
|
|
|
@ -4,20 +4,24 @@ import LayoutLens.Prelude
|
||||||
|
|
||||||
import Data.Array as Array
|
import Data.Array as Array
|
||||||
import Data.String (Pattern(..))
|
import Data.String (Pattern(..))
|
||||||
import LayoutLens.Data.Geometry (Geometry(..), Attribute(..), Attributes, GenericAttributes, PathStep(..))
|
import LayoutLens.Data.Geometry (Attribute(..), Attributes, GenericAttributes, Geometry(..), PathStep(..), boundingPolygon)
|
||||||
import LayoutLens.Data.Vec2 (AABB(..), ScalePreservingTransform(..), Vec2(..), radiansToDegrees, x, y)
|
import LayoutLens.Data.Vec2 (AABB(..), Vec2(..), boundingBox, x, y)
|
||||||
|
import LayoutLens.Data.Vec2 as V
|
||||||
|
|
||||||
indent :: String -> String
|
indent :: String -> String
|
||||||
indent = split (Pattern "\n") >>> map (" " <> _) >>> unlines
|
indent = split (Pattern "\n") >>> map (" " <> _) >>> unlines
|
||||||
|
|
||||||
|
printAttributes :: GenericAttributes -> String
|
||||||
|
printAttributes attributes = joinWith " "
|
||||||
|
$ uncurry (\key value -> fold [ key, "=\"", value, "\"" ])
|
||||||
|
<$> attributes
|
||||||
|
|
||||||
tag :: String -> GenericAttributes -> String -> String
|
tag :: String -> GenericAttributes -> String -> String
|
||||||
tag name attributes child = fold
|
tag name attributes child = fold
|
||||||
[ "<"
|
[ "<"
|
||||||
, name
|
, name
|
||||||
, " "
|
, " "
|
||||||
, joinWith " "
|
, printAttributes attributes
|
||||||
$ uncurry (\key value -> fold [ key, "=\"", value, "\"" ])
|
|
||||||
<$> attributes
|
|
||||||
, ">"
|
, ">"
|
||||||
, indent child
|
, indent child
|
||||||
, "</"
|
, "</"
|
||||||
|
@ -26,12 +30,19 @@ tag name attributes child = fold
|
||||||
]
|
]
|
||||||
|
|
||||||
leaf :: String -> GenericAttributes -> String
|
leaf :: String -> GenericAttributes -> String
|
||||||
leaf name attributes = tag name attributes mempty
|
leaf name attributes = fold
|
||||||
|
[ "<"
|
||||||
|
, name
|
||||||
|
, " "
|
||||||
|
, printAttributes attributes
|
||||||
|
, "/>"
|
||||||
|
]
|
||||||
|
|
||||||
printGeometryAttributes :: Attributes -> GenericAttributes
|
printGeometryAttributes :: Attributes -> GenericAttributes
|
||||||
printGeometryAttributes = map case _ of
|
printGeometryAttributes = map case _ of
|
||||||
Fill color -> "fill" /\ toHexString color
|
Fill color -> "fill" /\ toHexString color
|
||||||
Stroke color -> "stroke" /\ toHexString color
|
Stroke color -> "stroke" /\ toHexString color
|
||||||
|
StrokeWidth number -> "stroke-width" /\ show number
|
||||||
|
|
||||||
px :: Number -> String
|
px :: Number -> String
|
||||||
px n = show n <> "px"
|
px n = show n <> "px"
|
||||||
|
@ -39,6 +50,7 @@ px n = show n <> "px"
|
||||||
-- Render a geometry to svg
|
-- Render a geometry to svg
|
||||||
renderGeometry :: Geometry -> String
|
renderGeometry :: Geometry -> String
|
||||||
renderGeometry = case _ of
|
renderGeometry = case _ of
|
||||||
|
Invisible _ -> ""
|
||||||
Many array -> unlines $ renderGeometry <$> array
|
Many array -> unlines $ renderGeometry <$> array
|
||||||
|
|
||||||
Rect (AABB aabb) proper ->
|
Rect (AABB aabb) proper ->
|
||||||
|
@ -47,8 +59,8 @@ renderGeometry = case _ of
|
||||||
<>
|
<>
|
||||||
[ "x" /\ show (x aabb.position)
|
[ "x" /\ show (x aabb.position)
|
||||||
, "y" /\ show (y aabb.position)
|
, "y" /\ show (y aabb.position)
|
||||||
, "width" /\ px (x aabb.size)
|
, "width" /\ show (x aabb.size)
|
||||||
, "height" /\ px (y aabb.size)
|
, "height" /\ show (y aabb.size)
|
||||||
]
|
]
|
||||||
|
|
||||||
Path steps proper ->
|
Path steps proper ->
|
||||||
|
@ -78,12 +90,37 @@ renderGeometry = case _ of
|
||||||
(generic <> printGeometryAttributes proper)
|
(generic <> printGeometryAttributes proper)
|
||||||
string
|
string
|
||||||
|
|
||||||
Transform (ScalePreservingTransform transform) g ->
|
Transform (V.Transform transform) g ->
|
||||||
tag "g"
|
tag "g"
|
||||||
[ "transform" /\ fold
|
[ "transform" /\ joinWith " "
|
||||||
[ "rotate("
|
[ "matrix("
|
||||||
, show $ radiansToDegrees transform.rotation
|
, show $ transform.scale * cos (unwrap transform.rotation)
|
||||||
|
, show $ transform.scale * sin (unwrap transform.rotation)
|
||||||
|
, show $ transform.scale * -sin (unwrap transform.rotation)
|
||||||
|
, show $ transform.scale * cos (unwrap transform.rotation)
|
||||||
|
, show $ x transform.position
|
||||||
|
, show $ y transform.position
|
||||||
, ")"
|
, ")"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
$ renderGeometry g
|
$ renderGeometry g
|
||||||
|
|
||||||
|
-- | Adds the necessary boilerplate to store svg inside a file
|
||||||
|
makeSvgDocument :: Geometry -> String
|
||||||
|
makeSvgDocument geometry = tag "svg" attributes $ renderGeometry geometry
|
||||||
|
where
|
||||||
|
attributes =
|
||||||
|
[ "xmlns" /\ "http://www.w3.org/2000/svg"
|
||||||
|
, "xmlns:xlink" /\ "http://www.w3.org/1999/xlink"
|
||||||
|
]
|
||||||
|
<>
|
||||||
|
case boundingBox <$> boundingPolygon geometry of
|
||||||
|
Nothing -> []
|
||||||
|
Just (AABB box) -> pure
|
||||||
|
$ "viewBox"
|
||||||
|
/\ joinWith " "
|
||||||
|
[ show $ x box.position
|
||||||
|
, show $ y box.position
|
||||||
|
, show $ x box.size
|
||||||
|
, show $ y box.size
|
||||||
|
]
|
||||||
|
|
|
@ -2,10 +2,16 @@ module LayoutLens.Data.Vec2 where
|
||||||
|
|
||||||
import LayoutLens.Prelude
|
import LayoutLens.Prelude
|
||||||
|
|
||||||
|
import Data.Array.NonEmpty as NEA
|
||||||
|
import Partial.Unsafe (unsafePartial)
|
||||||
|
|
||||||
newtype Radians = Radians Number
|
newtype Radians = Radians Number
|
||||||
data Vec2 = Vec2 Number Number
|
data Vec2 = Vec2 Number Number
|
||||||
|
|
||||||
-- {{{ Base helpers
|
-- {{{ Base helpers
|
||||||
|
-- | Multiply by the matrix
|
||||||
|
-- | cos Θ -sin Θ
|
||||||
|
-- | sin Θ cos Θ
|
||||||
rotateBy :: Radians -> Vec2 -> Vec2
|
rotateBy :: Radians -> Vec2 -> Vec2
|
||||||
rotateBy (Radians angle) (Vec2 x y) = Vec2 (x * c - y * s) (x * s + y * c)
|
rotateBy (Radians angle) (Vec2 x y) = Vec2 (x * c - y * s) (x * s + y * c)
|
||||||
where
|
where
|
||||||
|
@ -30,25 +36,46 @@ x (Vec2 x _) = x
|
||||||
y :: Vec2 -> Number
|
y :: Vec2 -> Number
|
||||||
y (Vec2 _ y) = y
|
y (Vec2 _ y) = y
|
||||||
|
|
||||||
|
origin :: Vec2
|
||||||
|
origin = Vec2 0.0 0.0
|
||||||
|
|
||||||
-- }}}
|
-- }}}
|
||||||
-- {{{ Shapes
|
-- {{{ Shapes
|
||||||
newtype AABB = AABB { position :: Vec2, size :: Vec2 }
|
newtype AABB = AABB { position :: Vec2, size :: Vec2 }
|
||||||
newtype Polygon = Polygon (Array Vec2)
|
newtype Polygon = Polygon (NonEmptyArray Vec2)
|
||||||
|
|
||||||
aabbToPolygon :: AABB -> Polygon
|
aabbToPolygon :: AABB -> Polygon
|
||||||
aabbToPolygon (AABB aabb@{ size: Vec2 sx sy }) = Polygon
|
aabbToPolygon (AABB aabb@{ size: Vec2 sx sy }) = Polygon
|
||||||
|
$ unsafePartial -- Safe because the array always has four elements
|
||||||
|
$ fromJust
|
||||||
|
$ NEA.fromArray
|
||||||
[ aabb.position
|
[ aabb.position
|
||||||
, aabb.position <> Vec2 0.0 sy
|
, aabb.position <> Vec2 0.0 sy
|
||||||
, aabb.position <> aabb.size
|
, aabb.position <> aabb.size
|
||||||
, aabb.position <> Vec2 sx 0.0
|
, aabb.position <> Vec2 sx 0.0
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | The left-inverse of `aabbToPolygon`
|
||||||
|
boundingBox :: Polygon -> AABB
|
||||||
|
boundingBox (Polygon points) = AABB
|
||||||
|
{ position: Vec2 minX minY
|
||||||
|
, size: Vec2 (maxX - minX) (maxY - minY)
|
||||||
|
}
|
||||||
|
where
|
||||||
|
minX = NEA.foldl1 min $ x <$> points
|
||||||
|
minY = NEA.foldl1 min $ y <$> points
|
||||||
|
maxX = NEA.foldl1 max $ x <$> points
|
||||||
|
maxY = NEA.foldl1 max $ y <$> points
|
||||||
|
|
||||||
mapPoints :: (Vec2 -> Vec2) -> (Polygon -> Polygon)
|
mapPoints :: (Vec2 -> Vec2) -> (Polygon -> Polygon)
|
||||||
mapPoints f (Polygon points) = Polygon $ f <$> points
|
mapPoints f (Polygon points) = Polygon $ f <$> points
|
||||||
|
|
||||||
aabbCenter :: AABB -> Vec2
|
aabbCenter :: AABB -> Vec2
|
||||||
aabbCenter (AABB aabb) = aabb.position <> vscale 0.5 aabb.size
|
aabbCenter (AABB aabb) = aabb.position <> vscale 0.5 aabb.size
|
||||||
|
|
||||||
|
originAabb :: Vec2 -> AABB
|
||||||
|
originAabb size = AABB { position: origin, size }
|
||||||
|
|
||||||
-- }}}
|
-- }}}
|
||||||
-- {{{ Transforms
|
-- {{{ Transforms
|
||||||
newtype RawScalePreservingTransform = RawScalePreservingTransform
|
newtype RawScalePreservingTransform = RawScalePreservingTransform
|
||||||
|
@ -62,24 +89,72 @@ newtype ScalePreservingTransform = ScalePreservingTransform
|
||||||
, rotation :: Radians
|
, rotation :: Radians
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeScalePreservingTransform
|
newtype Transform = Transform
|
||||||
:: RawScalePreservingTransform -> ScalePreservingTransform
|
{ scale :: Number
|
||||||
normalizeScalePreservingTransform (RawScalePreservingTransform t) = ScalePreservingTransform
|
, position :: Vec2
|
||||||
{ rotation: t.rotateBy
|
, rotation :: Radians
|
||||||
, position: t.rotateAround <> rotateBy t.rotateBy (vsub t.position t.rotateAround)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Intuitivey, the raw transformation has form
|
||||||
|
-- | v ↦ r_Θ(v + a) - a + p,
|
||||||
|
-- | but rotations are linear, so the above can be rewritten as
|
||||||
|
-- | v ↦ r_Θ(v) + (r_Θ(a) - a + p),
|
||||||
|
-- | which is what this function does.
|
||||||
|
-- |
|
||||||
|
-- | The other case works similarly
|
||||||
|
normalizeScalePreservingTransform
|
||||||
|
:: RawScalePreservingTransform -> ScalePreservingTransform
|
||||||
|
normalizeScalePreservingTransform (RawScalePreservingTransform t) =
|
||||||
|
ScalePreservingTransform
|
||||||
|
{ rotation: t.rotateBy
|
||||||
|
, position: t.rotateAround
|
||||||
|
<> rotateBy t.rotateBy (t.position <> vinv t.rotateAround)
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | We want to compose
|
||||||
|
-- | f(v) = r_Θ(v) + p_f
|
||||||
|
-- | s(v) = r_ϕ(v) + p_s,
|
||||||
|
-- | which yields
|
||||||
|
-- | (s ∘ f)(v) = r_ϕ(r_Θ(v) + p_f) + p_s
|
||||||
|
-- | = r_ϕ(r_Θ(v)) + r_ϕ(p_f) + p_s
|
||||||
|
-- | = r_(ϕ+Θ)(v) + (r_ϕ(p_f) + p_s)
|
||||||
composeScalePreservingTransforms
|
composeScalePreservingTransforms
|
||||||
:: ScalePreservingTransform -> ScalePreservingTransform -> ScalePreservingTransform
|
:: ScalePreservingTransform -> ScalePreservingTransform -> ScalePreservingTransform
|
||||||
composeScalePreservingTransforms (ScalePreservingTransform first) (ScalePreservingTransform second) =
|
composeScalePreservingTransforms (ScalePreservingTransform second) (ScalePreservingTransform first) =
|
||||||
ScalePreservingTransform
|
ScalePreservingTransform
|
||||||
{ rotation: first.rotation <> second.rotation
|
{ rotation: first.rotation <> second.rotation
|
||||||
, position: second.position <> rotateBy second.rotation first.position
|
, position: second.position <> rotateBy second.rotation first.position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forgetScalePreservingStructure :: ScalePreservingTransform -> Transform
|
||||||
|
forgetScalePreservingStructure (ScalePreservingTransform transform) =
|
||||||
|
Transform
|
||||||
|
{ position: transform.position
|
||||||
|
, rotation: transform.rotation
|
||||||
|
, scale: 1.0
|
||||||
|
}
|
||||||
|
|
||||||
applyScalePreservingTransform :: ScalePreservingTransform -> Vec2 -> Vec2
|
applyScalePreservingTransform :: ScalePreservingTransform -> Vec2 -> Vec2
|
||||||
applyScalePreservingTransform (ScalePreservingTransform transform) v =
|
applyScalePreservingTransform = forgetScalePreservingStructure >>> applyTransform
|
||||||
rotateBy transform.rotation v <> transform.position
|
|
||||||
|
applyTransform :: Transform -> Vec2 -> Vec2
|
||||||
|
applyTransform (Transform transform) v =
|
||||||
|
-- Rotations are linear, thus they commute with scaling.
|
||||||
|
vscale transform.scale (rotateBy transform.rotation v) <> transform.position
|
||||||
|
|
||||||
|
tScale :: Number -> Transform
|
||||||
|
tScale factor = Transform
|
||||||
|
{ scale: factor
|
||||||
|
, position: origin
|
||||||
|
, rotation: mempty
|
||||||
|
}
|
||||||
|
|
||||||
|
tTranslate :: Vec2 -> Transform
|
||||||
|
tTranslate position = Transform
|
||||||
|
{ scale: 1.0
|
||||||
|
, rotation: mempty
|
||||||
|
, position
|
||||||
|
}
|
||||||
|
|
||||||
-- }}}
|
-- }}}
|
||||||
|
|
||||||
|
@ -89,12 +164,14 @@ derive instance Eq AABB
|
||||||
derive instance Eq Polygon
|
derive instance Eq Polygon
|
||||||
derive instance Eq RawScalePreservingTransform
|
derive instance Eq RawScalePreservingTransform
|
||||||
derive instance Eq ScalePreservingTransform
|
derive instance Eq ScalePreservingTransform
|
||||||
|
derive instance Eq Transform
|
||||||
derive instance Generic Vec2 _
|
derive instance Generic Vec2 _
|
||||||
derive instance Generic Radians _
|
derive instance Generic Radians _
|
||||||
derive instance Generic AABB _
|
derive instance Generic AABB _
|
||||||
derive instance Generic Polygon _
|
derive instance Generic Polygon _
|
||||||
derive instance Generic RawScalePreservingTransform _
|
derive instance Generic RawScalePreservingTransform _
|
||||||
derive instance Generic ScalePreservingTransform _
|
derive instance Generic ScalePreservingTransform _
|
||||||
|
derive instance Generic Transform _
|
||||||
|
|
||||||
instance Debug Vec2 where
|
instance Debug Vec2 where
|
||||||
debug = genericDebug
|
debug = genericDebug
|
||||||
|
@ -114,6 +191,9 @@ instance Debug RawScalePreservingTransform where
|
||||||
instance Debug ScalePreservingTransform where
|
instance Debug ScalePreservingTransform where
|
||||||
debug = genericDebug
|
debug = genericDebug
|
||||||
|
|
||||||
|
instance Debug Transform where
|
||||||
|
debug = genericDebug
|
||||||
|
|
||||||
instance Semigroup Vec2 where
|
instance Semigroup Vec2 where
|
||||||
append (Vec2 a b) (Vec2 c d) = Vec2 (a + c) (b + d)
|
append (Vec2 a b) (Vec2 c d) = Vec2 (a + c) (b + d)
|
||||||
|
|
||||||
|
@ -128,4 +208,4 @@ instance Monoid Vec2 where
|
||||||
instance Monoid Radians where
|
instance Monoid Radians where
|
||||||
mempty = Radians 0.0
|
mempty = Radians 0.0
|
||||||
|
|
||||||
derive newtype instance Monoid Polygon
|
derive instance Newtype Radians _
|
||||||
|
|
29
layout-lens/src/Generate/Svg.purs
Normal file
29
layout-lens/src/Generate/Svg.purs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module LayoutLens.Generate.Svg where
|
||||||
|
|
||||||
|
import LayoutLens.Prelude
|
||||||
|
|
||||||
|
import LayoutLens.Data.Config as C
|
||||||
|
import LayoutLens.Data.Geometry (Attribute(..))
|
||||||
|
import LayoutLens.Data.Geometry as G
|
||||||
|
import LayoutLens.Data.Svg as S
|
||||||
|
import LayoutLens.Data.Vec2 (forgetScalePreservingStructure, originAabb, tScale, vscale)
|
||||||
|
|
||||||
|
type SvgString = String
|
||||||
|
|
||||||
|
renderPhysicalKey :: C.PhysicalKey -> G.Attributes -> G.Geometry
|
||||||
|
renderPhysicalKey (C.PhysicalKey key) attributes = do
|
||||||
|
G.Transform (forgetScalePreservingStructure key.transform)
|
||||||
|
$ G.Rect (originAabb key.size) attributes
|
||||||
|
|
||||||
|
renderPhysicalLayout :: C.PhysicalLayout -> G.Geometry
|
||||||
|
renderPhysicalLayout (C.PhysicalLayout layout) =
|
||||||
|
G.Transform (tScale 100.0)
|
||||||
|
$ G.Many
|
||||||
|
$ flip renderPhysicalKey attributes
|
||||||
|
<$> layout
|
||||||
|
where
|
||||||
|
attributes =
|
||||||
|
[ Fill $ rgb 200 200 50
|
||||||
|
, Stroke black
|
||||||
|
, StrokeWidth 0.1
|
||||||
|
]
|
|
@ -2,16 +2,28 @@ module Main where
|
||||||
|
|
||||||
import LayoutLens.Prelude
|
import LayoutLens.Prelude
|
||||||
|
|
||||||
import LayoutLens.Data.Config (PhysicalLayout(..), buildConfig, buildPhysical)
|
import LayoutLens.Data.Config (LensConfig(..), buildConfig)
|
||||||
import LayoutLens.Data.RawConfig (RawConfig(..))
|
import LayoutLens.Data.Geometry (pad)
|
||||||
|
import LayoutLens.Data.Svg (makeSvgDocument)
|
||||||
|
import LayoutLens.Data.Vec2 (Vec2(..))
|
||||||
|
import LayoutLens.Generate.Svg (renderPhysicalLayout)
|
||||||
import LayoutLens.Parser (parseConfig)
|
import LayoutLens.Parser (parseConfig)
|
||||||
import Node.Encoding (Encoding(..))
|
import Node.Encoding (Encoding(..))
|
||||||
import Node.FS.Aff (readTextFile)
|
import Node.FS.Aff (readTextFile, writeTextFile)
|
||||||
|
|
||||||
main :: Effect Unit
|
main :: Effect Unit
|
||||||
main = launchAff_ do
|
main = launchAff_ do
|
||||||
file <- readTextFile UTF8 "../keyboards/qmk/ferris-sweep/config.lens"
|
file <- readTextFile UTF8 "../keyboards/qmk/ferris-sweep/config.lens"
|
||||||
|
-- file <- readTextFile UTF8 "./input.lens"
|
||||||
case parseConfig file of
|
case parseConfig file of
|
||||||
Left err -> log err
|
Left err -> log err
|
||||||
Right result -> do
|
Right result -> do
|
||||||
logPretty $ buildConfig result
|
let (LensConfig config) = buildConfig result
|
||||||
|
logPretty config
|
||||||
|
-- logPretty $ boundingPolygon $ renderPhysicalLayout config.physical
|
||||||
|
writeTextFile UTF8 "./output.svg"
|
||||||
|
$ makeSvgDocument
|
||||||
|
$ pad (Vec2 30.0 30.0)
|
||||||
|
$ renderPhysicalLayout
|
||||||
|
$ config.physical
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ name = ows *> P.try do
|
||||||
, "section"
|
, "section"
|
||||||
, "layer"
|
, "layer"
|
||||||
, "block"
|
, "block"
|
||||||
|
, "pre"
|
||||||
, "end"
|
, "end"
|
||||||
, "point"
|
, "point"
|
||||||
, "place"
|
, "place"
|
||||||
|
@ -151,7 +152,12 @@ physical = do
|
||||||
angle <- radians
|
angle <- radians
|
||||||
around <- P.option position vec2
|
around <- P.option position vec2
|
||||||
pure $ angle /\ around
|
pure $ angle /\ around
|
||||||
pure $ Place $ RawScalePreservingTransform { position, rotateBy, rotateAround }
|
pure $ Place $
|
||||||
|
RawScalePreservingTransform
|
||||||
|
{ position
|
||||||
|
, rotateBy
|
||||||
|
, rotateAround
|
||||||
|
}
|
||||||
|
|
||||||
point :: Parser RawPhysicalActionStep
|
point :: Parser RawPhysicalActionStep
|
||||||
point = do
|
point = do
|
||||||
|
@ -163,7 +169,11 @@ physical = do
|
||||||
let rotateAround = position
|
let rotateAround = position
|
||||||
let
|
let
|
||||||
point a b c d = Point
|
point a b c d = Point
|
||||||
{ transform: RawScalePreservingTransform { position: a, rotateBy: b, rotateAround: c }
|
{ transform: RawScalePreservingTransform
|
||||||
|
{ position: a
|
||||||
|
, rotateBy: b
|
||||||
|
, rotateAround: c
|
||||||
|
}
|
||||||
, size: d
|
, size: d
|
||||||
}
|
}
|
||||||
case arguments of
|
case arguments of
|
||||||
|
|
|
@ -26,6 +26,7 @@ module LayoutLens.Prelude
|
||||||
, module Data.Monoid.Generic
|
, module Data.Monoid.Generic
|
||||||
, module Data.String
|
, module Data.String
|
||||||
, module Data.List
|
, module Data.List
|
||||||
|
, module Data.Array.NonEmpty
|
||||||
, wrapInto
|
, wrapInto
|
||||||
, unimplemented
|
, unimplemented
|
||||||
, logPretty
|
, logPretty
|
||||||
|
@ -62,6 +63,7 @@ import Effect.Class.Console (clear, group, groupCollapsed, groupEnd, grouped, in
|
||||||
import Effect.Exception.Unsafe (unsafeThrow)
|
import Effect.Exception.Unsafe (unsafeThrow)
|
||||||
import Prim.TypeError (class Warn, Text)
|
import Prim.TypeError (class Warn, Text)
|
||||||
import Safe.Coerce (class Coercible, coerce)
|
import Safe.Coerce (class Coercible, coerce)
|
||||||
|
import Data.Array.NonEmpty (NonEmptyArray)
|
||||||
|
|
||||||
unimplemented :: forall a. Warn (Text "unimplemenet") => a
|
unimplemented :: forall a. Warn (Text "unimplemenet") => a
|
||||||
unimplemented = unsafeThrow "unimplemented"
|
unimplemented = unsafeThrow "unimplemented"
|
||||||
|
|
1
layout-lens/vim/ftdetect/lens.vim
Normal file
1
layout-lens/vim/ftdetect/lens.vim
Normal file
|
@ -0,0 +1 @@
|
||||||
|
au BufNewFile,BufRead *.lens set filetype=lens
|
|
@ -1,12 +1,12 @@
|
||||||
" if exists("b:current_syntax")
|
if exists("b:current_syntax")
|
||||||
" finish
|
finish
|
||||||
" endif
|
endif
|
||||||
|
|
||||||
set iskeyword+=-
|
set iskeyword+=-
|
||||||
|
|
||||||
syntax keyword lensKeyword physical section layergroup layer chordgroup block end
|
syntax keyword lensKeyword physical section layergroup layer chordgroup block end
|
||||||
syntax keyword lensAction sticky-switch switch
|
syntax keyword lensAction sticky-switch switch
|
||||||
syntax keyword lensFunction columns place action key
|
syntax keyword lensFunction columns place action key after before
|
||||||
syntax keyword lensLayerName center topleft topright bottomleft bottomright
|
syntax keyword lensLayerName center topleft topright bottomleft bottomright
|
||||||
|
|
||||||
syntax match lensComment "\v--.*$"
|
syntax match lensComment "\v--.*$"
|
||||||
|
|
Loading…
Reference in a new issue