An exploratory project, to learn and document typescript practices and tools, on the example of a spirolateral curve viewer webapp.
Although a visual project, the purpose is a familiarization with typescript and its environment. As such, the priorities are:
- Document the project and tools as starting point for future personal project: typescript.md
- Stick to typescript, avoid javascript where possible.
- Apply coding best practices and conventions, notably:
- Reference configuration of npm.
- A reasonably strict linter configuration (enforce format and comments).
- Unit tests.
- Avoid libraries / third party dependencies doing too much heavy lifting (including build dependencies - but with exception to what is needed for a best-practice build)
- Stick to popular tools, document tool selection and configuration: environment.md
- VSCode
- IDE plugins
- Node / npm plugins
The remainder of this page documents the spriolateral curve context and the elicited software design.
This program generates spirolateral curves, which can be generated using a turtle geometry model.
Imagine a turtle moving slowly forward, with a pen attached to its belly. Let's further consider that the turtle can only perform two actions: 1
- Move straight forward, for a given distance.
- Turn in place, for a given angle.
If our turtle keeps repeating the above two actions, we can trace the path it took on paper.
Even without a real turtle, we can attempt to generate the trace it's path, using a simple simulation.
Spirolateral curves are a special case of turtle graphics, i.e. they can be realized using a turtle graphic simulation.
In contrast to general turtle graphics, the turtle's actions are not arbitrary, but follow a specific algorithm.
- The turtle produces series of "motives".
- Each motive is itself a series of moves and turns, where:
- The initial move has length
1. - The length of every follow-up move within a motive is the next a natural multiple, i.e.
2,3,4, ... - The angle between two moves is constant, e.g. "10 degrees left".
- The initial move has length
This section details how concepts needed to generate and visualize spirolateral curves are translated to software components and behaviour.
Throughout the implementation, we'll use the following terminology:
-
heading: direction of the turtle's head, as clockwise offset from north in degrees. -
counter: the turtle's current count of steps within the current motive. -
amount: the amount of steps within a motive. -
angle: degrees by which the turtle will turn right after any move (including after completed motives).
---
title: Spirolateral Curve Generator
---
classDiagram
class CurveGenerator{
<<static>> generateCurve(initHead, initPos: Point, angle, amount):Curve
}
class Turtle{
-heading: int
-Position: Point
+Turtle(initX, initY, initHeading)
+advance(distance)
+turnClockwise(angle)
+getPosition():Point
+getOrientation():int
}
class Curve {
+addVertex(endPoint): void
+getVertexAmount(): number
+getVertex(index: number): Vertex
+get minX(): int
+get minY(): int
+get maxX(): int
+get maxY(): int
}
class Point{
+int x
+int y
}
class Vertex{
+start: Point
+end: Point
}
class CurveProcessor{
+process(curve):void
}
CurveGenerator ..> Turtle : generateCurve lets turtle advance and turn in motives
CurveGenerator ..> Curve : Produces description of spirolateral curve
CurveProcessor ..> Curve : Consumes description of spirolateral curve and builds svg
Curve *--> "1..*" Point : Spirolateral curve is composed of points, defining vertices
Curve ..> Vertex : Curves expose consecutive pairs of points as vertices.
Vertex o--> Point
Project components and relations. A
(Spirolateral) Curveobject is first produced, then consumed for visual presentation.
The main control flow consists of two phases:
- Generate a spirolateral curve: A
CurveGeneratoruses aTurtleinstance to incrementally produceCurveobject (composed of a series ofVertexobjects).- The
Turtleobject embodies only heading and position. The displacement strategy (how far to advance, angle to turn) is encoded in theCurveGenerator, which simply invokes theTurtles correspondingadvanceandturnmethods (see previous section, "Graphics"). - Every
Turtledisplacement holds a starting position (where theTurtlewas before moving) and an iteration ending position (where theTurtleis located after moving). The position pair defines aVertex, which will be eventually visualized.
- The
- Process a spirolateral curve: A
CurveProcessorafterwards consumes theCurveobject, to directly or indirectly visualize the total path taken by theTurtle.- The
SvgStringBuilderproduces a static file, which is stored on disk an can be inspected with an svg renderer, e.g. a browser. - The
SvgObjectBuilderdoes not operate on String level, but constructs or modifies an SVG object, for dynamic visualization, i.e. instant re-rendering onTurtleparameter changes.
- The
A non-trivial problem is when to stop the turtle. Turtle graphics, based on the initial definition are repetitive, but not necessarily contained in space.
Testing for when a turtle is back to its starting position and heading is insufficient for two reasons:
- Rounding errors prevent accurate testing.
- Some parameters produce non-circular traces, e.g. for
angle = 90,amount = 4, the turtle moves further away from the origin, with every motive.
To overcome the issue, we simply check on each completed motive, wether the turtle's heading matches the initial heading. If so, we stop the turtle. 2
Shape and size of the turtle's path vastly differ, depending on the two initial parameters (angle, amount).
A bullet proof strategy, is to first generate Curve object (containing a series of Vertexs), and keeping track of lowest and highest positions encountered within a Curve object. These boundaries can be used afterwards as input for the CurveProcessor instance, if centering or scaling is needed.
The curve generator is controlled via key commands:
0 .. 9: load preset.r: load random curve.q/w: decrease / increase initial heading.[/]: decrease / increase steps per motive.-/+: decrease / increase inter-step angle.
