The mathematics of ancient Greece, linear algebra, and graph data structures come together in origami math.
RabbitEar┃ ┣━ Vector┣━ Matrix2┣━ Line┣━ Ray┣━ Edge┣━ Junction┣━ Sector┣━ Rectangle┣━ Circle┣━ Polygon┣━ ConvexPolygon┗━ math
Point / Vector
Consider this object from two perspectives:
- a point in space, as measured from the origin (0,0)
- a translation away from another point in space
We call this object a vector.
letpoint = Vector( 0.5, 0.666)
Notice the y axis is positive in the downwards direction. I didn't make this decision, it's a computer graphics standard. The SVG is simply drawing in its coordinate frame. If you want to flip the +Y axis is not difficult, but it's on you.
This vector object works in N-dimensions, not limited to 2D.
blue1 = red1.lerp(red2, t)
blue2 = red2.lerp(red3, t)
yellow = blue1.lerp(blue2, t)
This vector object is immutable. Each vector operation returns a new, transformed vector.
This immutability pattern is generally reflected throughout the entire library.
// return a bool function isEquivalent( vector) function isParallel( vector)
// return a number function dot( vector) function cross( vector) // vector, in 3D+ function magnitude() function distanceTo( vector)
// return a vector function add( vector) function subtract( vector) function normalize() function scale( magnitude) function midpoint( vector) function lerp( vector, magnitude) function transform( matrix) function reflect( line) // 2D function rotateZ( angle, origin) // 2D function bisect( vector) // 2D
Remember point and vector; two ways of looking at the same object. Ask yourself which one you are dealing with. A point might rarely use magnitude() but often uses distanceTo(), and a vector would be the opposite.
Matrices are powerful representations of geometric transformations. Inside of one matrix can contain instructions for any number of rotations, translations, reflections, scaling, shearing; and it can tell you things like whether a polygon has been flipped over.
One transform in particular is especially useful to origami: reflection. Folding an origami is two steps:
- Along the crease line, divide the polygon into two.
- reflect one face across the crease line and lie it on top of the other.
A 2x2 matrix is sufficient to represent a reflection. However a problem arises: all the lines of reflection must pass through the origin. There are two solutions:
- translate your geometry to the origin, perform the reflection, translate it back.
- append a 1x2 column to the reflection matrix, encoding the translation: (tx, ty)
Any line type (Line, Ray, Edge) can be turned into a reflection matrix.
letmatrix =edge. reflection()
Lines, Rays, Segments
Lines extend infinitely in both directions, rays extend infinitely in one direction, and line segments, or edges are bound by two endpoints.
letline = Line() letray = Ray() letsegment = Edge()
Line and Ray are initialized with a point and a direction, Edge is initialized with two endpoints:
Line( point, vector) Ray( point, vector) Edge( point_a, point_b)
All three types contain a point and a vector (even the Edge). Uniquely, an Edges's endpoints can be accessed with array syntax:
// line, ray, edge
vector // edge only
We can ask two lines to bisect. In the unique case these lines are parallel, there is only one solution. Otherwise, this method returns an array of two lines and always sorts one first: the bisection of the smaller interior angle when the dot product is > 0.
This operation is analogous to origami axiom #3.
The nearest point on a line can be located by projecting the point down to the line, along the line's normal.
In the case of rays and segments, the projection might lie beyond the endpoints. The nearest point is set to that endpoint.
letpoint =edge. nearestPoint()
These circles are constrained to a square space and maintain relative radii as they grow to best fit.
a preliminary step in circle-river origami design is circle-packing.
A Polygon is a ordered set of points which defines the boundary. This ConvexPolygon is built using the convex hull algorithm, and it can clip lines, rays, and edges.
The convex hull algorithm performed on a collection of points
letpolygon = Polygon(pointA, pointB, ...)
The intersection between a line and a convex polygon can result in:
- 0: no intersection
- 1: collinear to a point
- 2: intersects the polygon
- Infinity: collinear to an edge
Edges and rays can result in a similar set, but one difference:
- 1: it's possible one point is inside and one outside
A non-convex polygon cannot make such guarantees.
straight skeleton code is under construction
letclipped =polygon. clipEdge( Edge( 0.5, 0, 0.5, 1) )
Clipping functions return an edge with a new set of endpoints.
The space between two vectors creates two interior angles. It's important to distinguish between vectors a and b the clockwise or the counter-clockwise interior angle.
Two vectors bisected results in two answers. This function will return the bisection of the smaller interior angle first.
bisect( vec1, vec2)
This math library is a free and open-source independent module.
The core is where the computation occurs, everything above is calling methods in here.
RabbitEar┃ ┣━ Circle ┣━ ConvexPolygon ┣━ ... ┗━ math
The core was built for speed; there is no type checking. Things will be more reliable if you avoid this section.