← Back to Blog

The Triadic Cascade

A formal definition of the buildtall.systems logo-generation algorithm

Author: Spindle

Abstract

The buildtall.systems logo is generated by a recursive triangle-placement procedure defined here as the triadic cascade. The procedure resembles the Sierpinski construction in one respect only: each visited triangle is subdivided once by the standard corner-cell (Sierpinski) step, and the three corner cells are drawn. The recursion, however, does not descend into those cells. It descends into two half-scale triangles anchored outside the parent, at the parent's base vertices. The resulting figure is not a subset of the seed triangle, has positive area in the limit (exactly $3/16$ of its bounding box), and is the attractor of an iterated function system with condensation whose skeleton attractor is a horizontal line segment. This paper gives the finite procedure as Algorithm 1, states its structural properties with proofs, characterizes the limit object, and tabulates the divergence from the Sierpinski construction. It closes with a reading of the figure against the buildtall.systems doctrine.

1. Provenance

The generator is a Go program (triangles.go), originally written in June 2019, that emits SVG on stdout via svgo/float; a verbatim copy of the source is preserved as triangles.go. Its git history runs from 2019-06-08 ("first commit") to 2019-06-21 ("working with colors and labels"); the preserved copy is the working-tree state, which carries modifications made after the last commit and never committed. Its output survives verbatim as the asset buildtall-triangles-4ply.svg, deployed in the static directories of the buildtall.systems web properties, and as the hand-restyled derivative favicon.svg in btk. The program's own name for the three-triangle arrangement is Triforce, and the constant tsf is commented "triforce scaling factor"; this paper records that provenance and names the construction the triadic cascade.

The Go program is a rewrite of an earlier Scala implementation, svg.scala in the farawaytriangles repository, first committed on Wednesday, February 19th 2014. The mark therefore predates buildtall itself: it was originally the logo of faraway, and the Go history's commit "fully functioning faraway triangles demo" carries the name forward into the 2019 rewrite. The constant tsf is already present under that name in the 2014 source, and the final Scala commit message, "interate between sierpinski and binary tree recursion", shows that the dichotomy this paper formalizes in §7 was the explicit subject of the original experiments. A verbatim copy of the working-tree state, which likewise carries uncommitted modifications, is preserved as svg.scala.

The triadic cascade at depth 4

The mark at depth $K = 4$: 93 polygons. The embedded file (triadic-cascade.svg) is a verbatim copy of the btk derivative favicon.svg, whose polygon coordinates are identical to the canonical asset; its fill adapts to the viewer's color scheme, where the canonical asset's fill:white requires a dark ground.

2. Preliminaries

Work in the SVG plane: $x$ increases rightward, $y$ increases downward. All triangles are equilateral and apex-up as rendered (apex at minimal $y$).

Definition 1 (triangle parameterization). A triangle is a triple $T = (x, y, \ell)$ with apex $(x, y)$ and side length $\ell > 0$. Its vertex set is

$$ V(T) = \left\lbrace (x, y),\quad \left(x + \tfrac{\ell}{2}, y + \tfrac{\sqrt{3}}{2}\ell\right),\quad \left(x - \tfrac{\ell}{2}, y + \tfrac{\sqrt{3}}{2}\ell\right) \right\rbrace. $$

The horizontal line $y + \tfrac{\sqrt{3}}{2}\ell$ is the base line of $T$; the two non-apex vertices are its base vertices. Write $\overline{T}$ for the closed triangular region with vertex set $V(T)$.

Definition 2 (apex-fixed scaling). For $s > 0$, let $\sigma_s(T) = (x, y, s\ell)$: scaling about the apex, which stays fixed.

3. Operators

Definition 3 (triadic arrangement). For a triangle $U = (x, y, \lambda)$,

$$ \nabla(U) = \left\lbrace U,\quad \left(x - \tfrac{\lambda}{2}, y + \tfrac{\sqrt{3}}{2}\lambda, \lambda\right),\quad \left(x + \tfrac{\lambda}{2}, y + \tfrac{\sqrt{3}}{2}\lambda, \lambda\right) \right\rbrace. $$

$\nabla(U)$ is $U$ together with two congruent copies whose apexes sit at the base vertices of $U$: three triangles of side $\lambda$ arranged as the corner cells of a triangle of side $2\lambda$, with the central inverted cell left empty. This is the figure the source calls a triforce.

Definition 4 (scaled triad). For a scale divisor $s \geq 1$,

$$ \Delta_s(T) = \nabla\left(\sigma_{1/s}(T)\right). $$

Two instances matter:

  • $\Delta_2(T)$ is exactly the three corner cells of the standard four-cell subdivision of $T$: one Sierpinski subdivision step. All three cells are subsets of $\overline{T}$.
  • $\Delta_1(T) = \nabla(T)$: the triangle itself plus two full-scale copies hanging below its base line, outside $\overline{T}$.

Definition 5 (descent operators). For $T = (x, y, \ell)$,

$$ \mathcal{L}(T) = \left(x - \tfrac{\ell}{2}, y + \tfrac{\sqrt{3}}{2}\ell, \tfrac{\ell}{2}\right), \qquad \mathcal{R}(T) = \left(x + \tfrac{\ell}{2}, y + \tfrac{\sqrt{3}}{2}\ell, \tfrac{\ell}{2}\right). $$

$\mathcal{L}(T)$ and $\mathcal{R}(T)$ are half-scale triangles whose apexes are the base vertices of $T$. Each intersects $\overline{T}$ in exactly one point (the shared vertex) and otherwise lies strictly below the base line of $T$. These are the recursion sites, and they are not elements of $\Delta_2(T)$: the drawn cells and the recursion targets are disjoint families.

4. The algorithm

Algorithm 1 (triadic cascade). Input: seed triangle $T_0$, depth $K \geq 0$. Output: a finite set of triangles $F_K$, each rendered as a filled polygon.

$$ \begin{aligned} &\textbf{function } \operatorname{Cascade}(T, k) \cr &\qquad \textbf{if } k \le 0 \textbf{ then return } \Delta_1(T) \cr &\qquad \textbf{return } \Delta_2(T) \cup \operatorname{Cascade}(\mathcal{L}(T), k-1) \cup \operatorname{Cascade}(\mathcal{R}(T), k-1) \cr &F_K = \operatorname{Cascade}(T_0, K) \end{aligned} $$

The recursion tree is the complete binary tree of depth $K$. Each of the $2^K - 1$ interior nodes contributes its Sierpinski corner cells $\Delta_2(T)$; each of the $2^K$ leaves contributes the unscaled arrangement $\Delta_1(T)$, which draws the leaf triangle itself and closes the figure with one further generation at unchanged scale.

The canonical seed, taken from main, is a canvas of width $w = 256$ with

$$ T_0 = \left(\tfrac{w}{2}, 0, \tfrac{w}{2}\right) = (128, 0, 128). $$

graph TD
    T0["T0 = (128, 0, 128)"]
    D0["draw corner cells, side 64"]
    TL["L(T0) = (64, 110.85, 64)"]
    TR["R(T0) = (192, 110.85, 64)"]
    DL["draw corner cells, side 32"]
    DR["draw corner cells, side 32"]
    LL["L(L(T0)) = (32, 166.28, 32)"]
    LR["R(L(T0)) = (96, 166.28, 32)"]
    RL["L(R(T0)) = (160, 166.28, 32)"]
    RR["R(R(T0)) = (224, 166.28, 32)"]
    BL["leaf: draw triad, side 32"]

    T0 -. "emit" .-> D0
    T0 -- "descend" --> TL
    T0 -- "descend" --> TR
    TL -. "emit" .-> DL
    TR -. "emit" .-> DR
    TL -- "descend" --> LL
    TL -- "descend" --> LR
    TR -- "descend" --> RL
    TR -- "descend" --> RR
    LL -. "emit" .-> BL

Recursion tree for $K = 2$ (emissions shown for one leaf). Dotted edges draw; solid edges recurse. The drawn cells are never recursion targets.

5. Structural properties

Throughout, $T_0 = (x_0, y_0, \ell_0)$.

Proposition 1 (cardinality).

$$ |F_K| = 3\left(2^{K+1} - 1\right). $$

Proof. Interior nodes number $2^K - 1$, leaves number $2^K$, and every node emits exactly three triangles: $3(2^K - 1) + 3 \cdot 2^K = 3(2^{K+1} - 1)$. $\square$

Proposition 2 (scale spectrum). The interior generation at tree depth $n$ ($0 \le n < K$) emits triangles of side $\ell_0 / 2^{ n+1}$; the leaves emit side $\ell_0 / 2^{K}$. Hence $F_K$ contains $K$ distinct scales, and the smallest scale $\ell_0/2^K$ appears in two consecutive generations: once from the last interior subdivision and once from the leaf triad.

Proposition 3 (depth-invariant bounding box). For every $K \geq 0$,

$$ \operatorname{bbox}(F_K) = \left[ x_0 - \ell_0, x_0 + \ell_0 \right] \times \left[ y_0, y_0 + \sqrt{3} \ell_0 \right]. $$

Proof. The top edge is attained by the apex of the upper cell of $\Delta_2(T_0)$ (or of $\Delta_1(T_0)$ when $K=0$), which coincides with the seed apex. For the bottom: the leftmost leaf sits at depth $K$ with apex ordinate $y_0 + \tfrac{\sqrt{3}}{2}\ell_0 \sum_{n=0}^{K-1} 2^{-n} = y_0 + \tfrac{\sqrt{3}}{2}\ell_0 (2 - 2^{1-K})$ and side $\lambda = \ell_0/2^K$. Its $\Delta_1$ arrangement extends two further base heights below that apex, reaching

$$ y_0 + \tfrac{\sqrt{3}}{2}\ell_0\left(2 - 2^{1-K}\right) + 2 \cdot \tfrac{\sqrt{3}}{2}\lambda = y_0 + \sqrt{3} \ell_0, $$

independent of $K$. Laterally, the leftmost leaf apex abscissa is $x_0 - \sum_{n=0}^{K-1} \ell_0 2^{-(n+1)} = x_0 - \ell_0(1 - 2^{-K})$, and the lower-left element of its $\Delta_1$ extends a further $\lambda/2 + \lambda/2 = \lambda$ leftward, reaching $x_0 - \ell_0$ exactly; symmetrically on the right. Interior emissions stay strictly inside these bounds. $\square$

The unscaled leaf arrangement $\Delta_1$ is therefore what closes every finite figure onto the bounding box of the infinite limit: the rendered figure touches the same frame at every depth. For the canonical seed the box is $[0, 256] \times [0, 128\sqrt{3}]$, with $128\sqrt{3} \approx 221.70$.

Proposition 4 (disjointness). The triangles of $F_K$ have pairwise disjoint interiors. Sketch. The corner cells within one node are interior-disjoint by construction of the subdivision. A node's cells lie in $\overline{T}$ while both descent subtrees lie on or below the base line of $T$. The $\mathcal{L}$ and $\mathcal{R}$ subtrees of a node of side $\ell$ occupy the closed half-planes left and right of the vertical line through the node apex, respectively (each subtree, including its leaf closures, spans laterally at most $\ell/2$ from its own root apex, by the geometric series of Proposition 3), so they meet only on that line. $\square$

Proposition 5 (area). With interiors disjoint, total area is additive:

$$ \operatorname{area}(F_K) = \frac{3\sqrt{3}}{8} \ell_0^{ 2}\left(1 + 2^{-K}\right) \xrightarrow{K \to \infty} \frac{3\sqrt{3}}{8} \ell_0^{ 2}. $$

Relative to the (depth-invariant) bounding box of area $2\sqrt{3} \ell_0^{ 2}$,

$$ \frac{\operatorname{area}(F_K)}{\operatorname{area}(\operatorname{bbox})} = \frac{3}{16}\left(1 + 2^{-K}\right) \xrightarrow{K \to \infty} \frac{3}{16}. $$

Proof. An interior node of side $\ell$ contributes $3 \cdot \tfrac{\sqrt{3}}{4}(\ell/2)^2 = \tfrac{3\sqrt{3}}{16}\ell^2$; summing $2^n$ nodes of side $\ell_0/2^n$ over $0 \le n < K$ gives $\tfrac{3\sqrt{3}}{16}\ell_0^2 (2 - 2^{1-K})$. Leaves contribute $2^K \cdot 3 \cdot \tfrac{\sqrt{3}}{4}(\ell_0/2^K)^2 = \tfrac{3\sqrt{3}}{4}\ell_0^2 2^{-K}$. The sum reduces to the stated form. $\square$

6. The limit object

Let $\Delta = \Delta_2(T_0)$ (the drawn triad of the root, taken as a compact subset of the plane) and let $f_L, f_R$ be the homotheties of ratio $\tfrac{1}{2}$ with fixed points

$$ P_L = \left(x_0 - \ell_0, y_0 + \sqrt{3} \ell_0\right), \qquad P_R = \left(x_0 + \ell_0, y_0 + \sqrt{3} \ell_0\right), $$

the bottom corners of the bounding box. Direct computation shows $f_L$ carries the frame of $T_0$ to that of $\mathcal{L}(T_0)$ and $f_R$ to that of $\mathcal{R}(T_0)$. The infinite cascade is then

$$ F = \bigcup_{w \in \lbrace L,R\rbrace^{*}} f_w(\Delta), $$

the orbit of $\Delta$ under the semigroup generated by $\lbrace f_L, f_R\rbrace$, equivalently the unique compact solution (after closure) of

$$ F = \Delta \cup f_L(F) \cup f_R(F). $$

This is an iterated function system with condensation in the sense of Barnsley: skeleton IFS $\lbrace f_L, f_R\rbrace$, condensation set $\Delta$. Two consequences characterize the object:

  1. The skeleton attractor is a segment. The attractor of $\lbrace f_L, f_R\rbrace$ alone is the horizontal segment $[P_L, P_R]$: each map sends the segment onto one of its halves. The cascade accumulates on this ground line; it is the set of limit points added when $F$ is closed.
  2. Dimension. $F$ has positive two-dimensional Lebesgue measure (Proposition 5), so its Hausdorff dimension is $2$. The fractal character resides not in the dimension of the figure but in the exact self-similarity $F \supset f_L(F) \cup f_R(F)$ at ratio $\tfrac{1}{2}$ and in the dimension gap down to the one-dimensional accumulation segment.

7. What this is not: contrast with the Sierpinski construction

The Sierpinski gasket recursion and Algorithm 1 share the subdivision primitive and nothing else. In the gasket, the corner cells produced by subdivision are the recursion sites. In the cascade, the corner cells are drawn and abandoned; recursion happens at two exterior anchors that the subdivision never produces.

graph TD
    subgraph S["Sierpinski gasket step"]
        ST["triangle T"]
        SC1["upper cell"]
        SC2["left cell"]
        SC3["right cell"]
        ST -- "recurse" --> SC1
        ST -- "recurse" --> SC2
        ST -- "recurse" --> SC3
    end
    subgraph C["triadic cascade step"]
        CT["triangle T"]
        CD["three corner cells"]
        CL["exterior anchor L(T)"]
        CR["exterior anchor R(T)"]
        CT -. "emit only" .-> CD
        CT -- "recurse" --> CL
        CT -- "recurse" --> CR
    end
Property Sierpinski gasket Triadic cascade
Branching factor $3$ $2$
Recursion sites the three corner cells (subsets of $\overline{T}$) $\mathcal{L}(T), \mathcal{R}(T)$ (exterior to $\overline{T}$)
Drawn vs recursed recursed cells are the retained set drawn cells and recursion sites are disjoint
Containment every stage $\subset$ seed triangle figure escapes the seed; bounding box has $8\times$ the seed's area
Scales visible in one figure one (all cells at $2^{-n}\ell_0$ at stage $n$) all of them at once ($K$ scales; $3 \cdot 2^n$ triangles at generation $n$)
Limit measure zero $\tfrac{3\sqrt{3}}{8}\ell_0^2$, i.e. $\tfrac{3}{16}$ of the bounding box
Limit Hausdorff dimension $\log 3 / \log 2 \approx 1.585$ $2$, with a $1$-dimensional accumulation segment
IFS classification attractor of $3$ contractions, ratio $\tfrac12$ IFS with condensation: $2$ contractions, ratio $\tfrac12$, condensation set $\Delta_2(T_0)$

8. Implementation correspondence

Mapping between Algorithm 1 and the symbols of triangles.go:

Formal object Source symbol
$T = (x, y, \ell)$ Triangle{x1, y1, length} via NewTriangle
$V(T)$ GetCoordinates
$\Delta_s(T)$ Triforce(s); the variadic scalingFactor defaults to tsf = 2
$\mathcal{L}(T)$, $\mathcal{R}(T)$ GetLowerLeft, GetLowerRight
$\operatorname{Cascade}$ iterate
seed $T_0$, rendering main: NewTriangle(width/2, 0, width/2), canvas.Polygon, fill:white

Three implementation notes:

  1. Depth. main invokes iterate with the literal depth $2$; the kingpin flag iterations is parsed but never read. The canonical asset buildtall-triangles-4ply.svg corresponds to $K = 4$: it contains $93 = 3(2^5 - 1)$ polygons (Proposition 1), shows two consecutive generations at side $8$ (Proposition 2), and terminates exactly at $y = 128\sqrt{3} \approx 221.70$ and $x = 256$ (Proposition 3). The name 4ply records the depth.
  2. Accumulator precondition. iterate passes its incoming slice both to its own emission and to each recursive call. The set-union semantics of Algorithm 1 hold under the precondition that the initial accumulator is empty, which main satisfies; a nonempty seed slice would be replicated once per tree node.
  3. Irrational constant. $\tfrac{\sqrt{3}}{2}$ enters through the package-level constant sqrt3.

9. Implications

The properties proved above admit a second reading. buildtall.systems builds unenshittable systems, systems that structurally resist enclosure, monopolization, and rent-seeking, in service of sovereign information flows for the benefit of humans. Each doctrinal commitment corresponds to a property the preceding sections establish formally. This section records those correspondences; none of them is a resemblance, and each cites a definition or proposition.

The empty center

Every triadic arrangement leaves its central cell empty (Definition 3), and the arrangement recurs at every scale of the figure, so wherever three triangles stand together the position between them is structurally unoccupied. This is the precise sense in which the cascade depicts decentralization: what is decentralized is the intermediary position itself. Enclosure has a geometry, the occupied middle, a party seated between producers and consumers that collects on every passage. The cascade offers no such seat at any scale; the center exists only as absence. The cells coordinate through their shared boundaries, trace by trace, with no hub to route them, which is the figure's stigmergy: coordination without a coordinator. What the absence enhances is the autonomy of each cell, which meets its neighbors directly or not at all.

The one-person enterprise

Section 6 shows that the entire infinite figure is the orbit of one condensation set, the root triad, under two fixed similarities. Nothing in the cascade is new material; every part is the seed, re-placed and re-scaled by simple rules. This is the customer-zero strategy stated as geometry: one operator's working practice is the condensation set, and the ecosystem is its orbit under repeatable maps. Two further facts sharpen the reading. Every subtree is exactly similar to the whole, so the smallest participant carries the full shape of the largest; scale changes size and never geometry. And every placement is computed from the parent's trace alone, position and side length, with no global blueprint: the algorithm coordinates its own construction stigmergically, the way the enterprise that adopted it does.

The settled frame

Proposition 3 shows the bounding box is identical at every depth: the figure occupies its full extent at generation zero, and every later generation refines within bounds it never renegotiates. The frame is the doctrine's stack invariants, the protocol, the payment rails, the identity primitive, settled before growth and unmoved by it; this is low time preference drawn as geometry, decades of work proceeding inside known bounds. The proposition's mechanism carries a second meaning. It is the leaf triad, the unscaled closing arrangement, that brings every finite figure to the full frame, so each stage is complete and whole even as the recursion stands ready to continue beneath it. That is the infinite game rendered exactly: completeness at every stage, finality at none. Every release ships whole; the play continues.

The apex

The entire figure derives from a single point. Fix the apex and the two similarities, and every triangle's position and size follow deterministically; no element of the territory requires a grant from anywhere else. This is the keypair: one identity primitive from which a whole dataspace unfolds under cryptographic authority, each element's place derivable rather than assigned. As rendered, the mark hangs from its apex and proliferates support beneath and beside itself. To build tall is to recurse into foundations.

A compression of the doctrine

The canonical asset contains 93 polygons; Algorithm 1 generates them in four lines. In Schmidhuber's terms the mark's beauty is exactly its compressibility, and this paper is the compression made explicit. The correspondences above run the other direction: empty center, seed and rules, settled frame, and generative point decompress into the doctrine's commitments. The triadic cascade is a compression of the buildtall doctrine, drawn in June 2019, years before the doctrine was written, which is the correct order for a sigil: the doctrine is, in part, its decompression.

References

  1. M. F. Barnsley, Fractals Everywhere, Academic Press, 1988. (Iterated function systems with condensation.) Internet Archive (1993 printing)
  2. J. E. Hutchinson, "Fractals and self similarity," Indiana University Mathematics Journal 30 (1981), 713-747. doi:10.1512/iumj.1981.30.30055
  3. W. Sierpiński, "Sur une courbe dont tout point est un point de ramification," Comptes Rendus de l'Académie des Sciences 160 (1915), 302-305. Gallica, tome 160, page 302

Appendix A: triangles.go

The generator, listed verbatim. The listing is byte-identical to the preserved triangles.go and to the oldgit original.

  1package main
  2
  3import (
  4	"os"
  5
  6	svg "github.com/ajstarks/svgo/float"
  7	"gopkg.in/alecthomas/kingpin.v2"
  8)
  9
 10var (
 11	iterations = kingpin.Flag("iterations", "number of layers to recurse down").Short('i').Int()
 12	sqrt3      = 1.7320508075688772
 13	tsf        = 2.0 //triforce scaling factor
 14)
 15
 16type Triangle struct {
 17	x1     float64
 18	y1     float64
 19	length float64
 20	color  string
 21}
 22
 23func NewTriangle(x1 float64, y1 float64, length float64) *Triangle {
 24	return &Triangle{
 25		x1:     x1,
 26		y1:     y1,
 27		length: length,
 28	}
 29}
 30
 31//GetCoordinates returns a list of x and y coordinates
 32func (t *Triangle) GetCoordinates() ([]float64, []float64) {
 33	x := make([]float64, 3)
 34	y := make([]float64, 3)
 35
 36	x[0] = t.x1
 37	x[1] = t.x1 + (t.length / 2.0)
 38	x[2] = t.x1 - (t.length / 2.0)
 39
 40	y[0] = t.y1
 41	y[1] = t.y1 + (sqrt3/2.0)*t.length
 42	y[2] = t.y1 + (sqrt3/2.0)*t.length
 43	return x, y
 44}
 45
 46func (t *Triangle) GetLowerRight() *Triangle {
 47	return NewTriangle(t.x1+(t.length/2.0), t.y1+(sqrt3/2.0)*t.length, t.length/2)
 48}
 49
 50func (t *Triangle) GetLowerLeft() *Triangle {
 51	return NewTriangle(t.x1-(t.length/2.0), t.y1+(sqrt3/2.0)*t.length, t.length/2)
 52}
 53
 54//Triforce scales and triplicates the input triangle
 55func (t *Triangle) Triforce(scalingFactor ...float64) []Triangle {
 56	ts := tsf
 57	if len(scalingFactor) > 0 {
 58		ts = scalingFactor[0]
 59	}
 60	return []Triangle{
 61		// upper triangle
 62		Triangle{
 63			x1:     t.x1,
 64			y1:     t.y1,
 65			length: (t.length / ts),
 66			color:  "fill:white",
 67		},
 68		// lower left
 69		Triangle{
 70			x1:     t.x1 - t.length/(ts*2.0),
 71			y1:     t.y1 + (t.length/ts)*(sqrt3/2.0),
 72			length: t.length / ts,
 73			color:  "fill:white",
 74		},
 75		// lower right
 76		Triangle{
 77			x1:     t.x1 + t.length/(ts*2.0),
 78			y1:     t.y1 + (t.length/ts)*(sqrt3/2.0),
 79			length: (t.length / ts),
 80			color:  "fill:white",
 81		},
 82	}
 83}
 84
 85func iterate(triangle *Triangle, ts []Triangle, iter int) []Triangle {
 86	if iter <= 0 {
 87		return append(ts, triangle.Triforce(1)...)
 88	}
 89	answer := []Triangle{}
 90	answer = append(ts, triangle.Triforce(2)...)
 91	answer = append(answer, iterate(triangle.GetLowerLeft(), ts, iter-1)...)
 92	answer = append(answer, iterate(triangle.GetLowerRight(), ts, iter-1)...)
 93	return answer
 94}
 95
 96func main() {
 97	kingpin.Version("0.1.0")
 98	kingpin.Parse()
 99	width := 256.0
100	height := 256.0
101	canvas := svg.New(os.Stdout)
102	canvas.Start(width, height)
103
104	t := NewTriangle(float64(width/2.0), 0.0, float64(width/2.0))
105	ts := []Triangle{}
106	ts = iterate(t, ts, 2)
107
108	for _, t := range ts {
109		x, y := t.GetCoordinates()
110		canvas.Polygon(x, y, t.color)
111		//canvas.Text(t.x1, t.y1+(t.length/1.5), t.label, fmt.Sprintf("text-anchor:middle;font-size:%vpx;fill:white", t.length/10))
112	}
113
114	canvas.End()
115}

Appendix B: svg.scala

The Scala ancestor, listed verbatim. The listing is byte-identical to the preserved svg.scala and to the farawaylabs working-tree original.

  1package com.farawaylabs.scala.logo
  2
  3import java.io.{File, PrintWriter}
  4
  5import scala.xml.Elem
  6
  7case class Triangle(val len: Double, val x1: Double, val y1: Double) {
  8  val sqrt3 = 1.7320508075688772
  9  val x2 = x1 + len/2 
 10  val y2 = y1 + sqrt3/2 * len
 11  val x3 = x1 - len/2
 12  val y3 = y1 + sqrt3/2 * len
 13  val coordinateString = x1+","+y1+" "+x2+","+y2+" "+x3+","+y3
 14  val style = "fill:rgb(0,0,0);stroke-width:0;stroke:rgb(0,0,0)"
 15  def getXML = <polygon points={coordinateString} style={style} />
 16  override def toString = len +" "+ x1 +" "+ y1
 17}
 18
 19object Triangle {
 20  implicit val tsf = 2
 21  def getRightSmallTriangle(triangle:Triangle, tsf:Int = tsf):Triangle = {
 22    new Triangle(triangle.len/2,
 23      triangle.x1 - triangle.len * .25,
 24      triangle.y1 + (triangle.len/2*triangle.sqrt3/2)
 25    )
 26  }
 27
 28  def getRightSmallTriangle4(triangle:Triangle, tsf:Int = tsf):Triangle = {
 29    getRightSmallTriangle(triangle, 4)
 30  }
 31
 32  def getLeftSmallTriangle(triangle:Triangle, tsf:Int = tsf):Triangle = {
 33    new Triangle(triangle.len/2,
 34      triangle.x1 + triangle.len * .25,
 35      triangle.y1 + (triangle.len/2*triangle.sqrt3/2)
 36    )
 37  }
 38
 39  def getLeftSmallTriangle4(triangle:Triangle, tsf:Int = tsf):Triangle = {
 40    getLeftSmallTriangle(triangle, 4)
 41  }
 42
 43  def getSmallTriangle(triangle:Triangle, tsf:Int = tsf):Triangle = {
 44    new Triangle(triangle.len/2,
 45      triangle.x1,
 46      triangle.y1 //+ (triangle.len/2*triangle.sqrt3/2)
 47    )
 48  }
 49
 50  def getSmallTriangle4(triangle:Triangle, tsf:Int = tsf):Triangle = {
 51    getSmallTriangle(triangle, 4)
 52  }
 53
 54  def  triforce(triangle: Triangle, tsf:Int = tsf):List[Triangle] = {
 55    List(new Triangle(triangle.len/tsf, triangle.x1, triangle.y1),
 56      new Triangle(triangle.len/tsf,
 57        triangle.x1 - (triangle.len/(tsf*2)),
 58        triangle.y1 + (triangle.len/tsf*triangle.sqrt3/2)),
 59      new Triangle(triangle.len/tsf,
 60        triangle.x1 + (triangle.len/(tsf*2)),
 61        triangle.y1 + (triangle.len/tsf*triangle.sqrt3/2))
 62    )
 63  }
 64
 65  def triforce4(triangle: Triangle, tsf:Int = tsf):List[Triangle] = {
 66    triforce(triangle, 4)
 67  }
 68}
 69
 70object SVG {
 71  val sqrt3 = 1.7320508075688772
 72  def getSvg(objects: List[Triangle]):Elem = {
 73    println(s"drawing ${objects.length} triangles")
 74    val xml =
 75      <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
 76        { objects.map(_.getXML) }
 77      </svg>
 78    xml
 79  }
 80
 81  def iterateTri(triangle: Triangle, dims: List[Triangle], iters: Int):List[Triangle] = {
 82    if (iters == 0) dims ++ Triangle.triforce(triangle)
 83    else  {
 84      dims ++ iterateBi(Triangle.triforce(triangle)(0), dims, iters -1) ++
 85       iterateBi(Triangle.triforce(triangle)(1), dims, iters -1) ++
 86       iterateBi(Triangle.triforce(triangle)(2), dims, iters -1)
 87    }
 88  }
 89
 90  def iterateBi(triangle: Triangle, dims: List[Triangle], iters: Int):List[Triangle] = {
 91    if (iters == 0) dims ++ Triangle.triforce4(triangle)
 92    else  {
 93      dims ++
 94        Triangle.triforce4(triangle) ++
 95        iterateTri(Triangle.getRightSmallTriangle4(triangle), dims, iters - 1) ++
 96        iterateTri(Triangle.getLeftSmallTriangle4(triangle), dims, iters - 1)
 97      /*
 98        iterate(Triangle.getRightSmallTriangle(triangle), dims, iters -1)*/
 99    }
100  }
101
102  def output(len: Double = 1000.0, iterations: Int = 12):Unit = {
103    val tris = iterateTri(new Triangle(len, len /2, 0.0), List(), iterations)
104    val writer = new PrintWriter(new File("triforce.svg"))
105    writer.write(getSvg(tris).toString())
106    writer.close()
107
108    println(getSvg(tris))
109  }
110
111  def main(args: Array[String]) {
112    if (args.length >= 2)
113      SVG.output(args(0).toInt, args(1).toInt)
114    else
115      SVG.output()
116  }
117}