Add initial files

This commit is contained in:
Nathan McRae
2025-07-14 18:21:18 -07:00
commit 60a0cc74a3
5 changed files with 283 additions and 0 deletions

131
src/Main.purs Normal file
View File

@@ -0,0 +1,131 @@
module Main where
import Prelude (discard, class Monoid, class Semigroup, Unit, ($), (<>), (>>>))
import Data.Array (range, cons)
import Data.Field (div)
import Data.Functor (map)
import Data.Int (toNumber)
import Data.Number (cos, pi, sin)
import Data.Number.Format (toString)
import Data.List (fold)
import Data.Ord ((<))
import Data.Ring (add, mul, negate, sub)
import Data.Tuple.Nested (Tuple3, tuple3, get1, get2, get3)
import Effect (Effect)
import Effect.Console (log)
type Point =
{ x :: Number
, y :: Number
}
type Line =
{ start :: Point
, end :: Point
}
-- TODO: How to do more normal math expressions?
rotate :: Number -> Point -> Point
rotate angle { x: x, y: y} = {
x: sub (mul (cos angle) x) (mul (sin angle) y),
y: add (mul (sin angle) x) (mul (cos angle) y)
}
transform :: Number -> Number -> Number -> Point -> Point
transform scale xOffset yOffset { x: x, y: y} = {
x: (add (mul scale x) xOffset),
y: (add (mul scale y) yOffset)
}
rotateLine :: Number -> Line -> Line
rotateLine angle { start: p1, end: p2} = {
start: rotate angle p1,
end: rotate angle p2
}
transformLine :: Number -> Number -> Number -> Line -> Line
transformLine scale xOffset yOffset { start: start, end: end } = {
start : t start,
end : t end
}
where t = transform scale xOffset yOffset
-- How to handle the different coordinate systems?
-- These things should be generated in the basic coordinate system
-- * ticks via generateTicks
-- * axis labels
-- * axis lines
-- There should be a transform function that these should be passed through directly after generation
newtype XMLFragment = XMLFragment String
derive newtype instance xmlFragmentSemiGroup :: Semigroup XMLFragment
derive newtype instance xmlFragmentMonoid :: Monoid XMLFragment
-- TODO: Add style configuration
svgLine :: Line -> XMLFragment
svgLine { start: {x: x1, y: y1}, end: {x: x2, y: y2} } =
XMLFragment $ """<path style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
""" <> (toString x1) <> "," <> (toString y1) <> " " <> (toString x2) <> "," <> (toString y2) <> "\"/>"
-- TODO: Make axis tick size a parameter
getTick :: Number -> Int -> Int -> Line
getTick scale numTicks tickI =
{start: {x: x, y: negate (add 0.5 (div 5.0 scale))}, end: {x: x, y: y}}
where
x = (sub (div (mul 2.0 (mul (sin (div pi 3.0)) (toNumber tickI))) (toNumber numTicks)) (sin (div pi 3.0)))
y = if tickI < (div numTicks 2)
then (add 1.0 (mul x (div 1.5 (sin (div pi 3.0)))))
else (sub 1.0 (mul x (div 1.5 (sin (div pi 3.0)))))
getTicks :: Number -> Number -> Int -> Tuple3 (Array Line) (Array Line) (Array Line)
getTicks scale angle numTicks = --tuple3 [] [] []
tuple3 axis1Lines axis2Lines axis3Lines
where
foo = map (getTick scale numTicks) (range 0 numTicks)
axis1Lines = map (rotateLine angle) foo
axis2Lines = map (rotateLine (mul 2.0 (div pi 3.0))) axis1Lines
axis3Lines = map (rotateLine (mul 2.0 (div pi 3.0))) axis2Lines
-- There's probably a better way to do this
unfragment :: XMLFragment -> String
unfragment (XMLFragment frag) = frag
ternaryGraphSvg :: Array XMLFragment -> String
ternaryGraphSvg fragments = """<?xml version="1.0" encoding="UTF-8"?>
<svg
width="150mm"
height="120mm"
viewBox="-50 -50 200 200"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:svg="http://www.w3.org/2000/svg">
""" <> unfragment (fold fragments) <> "</svg>"
axesPoints :: Number -> Array Point
axesPoints angle = [
rotate angle { x: 0, y: 1 },
rotate angle { x: (negate (sin (mul 2.0 (div pi 3.0)))), y : (negate 0.5)},
rotate angle { x: (sin (mul (div pi 3.0))), y: (negate 0.5) }
]
ternaryGraph :: Number -> Number -> Number -> Int -> String
ternaryGraph scale xOffset yOffset numTicks = ternaryGraphSvg [tickLinesSvg, axesPathSvg, axisTitleSvg, tickLabelsSvg]
where
axisTickLines = getTicks scale pi numTicks
axis1TickLines = get1 axisTickLines
axis2TickLines = get2 axisTickLines
axis3TickLines = get3 axisTickLines
tickLines = (cons axis1TickLines (cons axis2TickLines axis3TickLines))
transformMyLine = transformLine scale xOffset yOffset
tickLinesSvg = fold $ map (svgLine >>> transformMyLine) tickLines
axesPathSvg =
main :: Effect Unit
main = do
log $ ternaryGraphSvg [XMLFragment "<asdf/>", XMLFragment "<foobar/>"]
log $ toString $ toNumber (div 5 3)