diff --git a/public/index.html b/public/index.html index 5288ba7..3f2f32c 100644 --- a/public/index.html +++ b/public/index.html @@ -39,12 +39,8 @@

Inward spiral

-

Regular

-
-
-
-

Randomized

-
+

Square

+

Triangle

diff --git a/public/index.html b/public/index.html index 5288ba7..3f2f32c 100644 --- a/public/index.html +++ b/public/index.html @@ -39,12 +39,8 @@

Inward spiral

-

Regular

-
-
-
-

Randomized

-
+

Square

+

Triangle

diff --git a/src/BaseShapes.ts b/src/BaseShapes.ts index dacf94c..e89d649 100644 --- a/src/BaseShapes.ts +++ b/src/BaseShapes.ts @@ -6,26 +6,36 @@ const center = new Point(max / 2, max / 2); const hexagon = createHexagonInSquare(max); const lines = zip(hexagon, hexagon, 1); - const base = lines.map(([a, b]) => { - return new Polygon(...[ - a, b, center, - ]); - }); + const hexagonTriangles = lines.map(([a, b]) => [a, b, center]); + const corner1 = new Point(max, 0); const corner2 = new Point(0, 0); const corner3 = new Point(0, max); const corner4 = new Point(max, max); - base.push(new Polygon(...[hexagon[0], corner1, hexagon[1]])); - base.push(new Polygon(...[hexagon[2], corner2, hexagon[3]])); - base.push(new Polygon(...[hexagon[3], corner3, hexagon[4]])); - base.push(new Polygon(...[hexagon[5], corner4, hexagon[0]])); - return base; + const cornerTriangles = [ + [hexagon[0], corner1, hexagon[1]], + [hexagon[2], corner2, hexagon[3]], + [hexagon[3], corner3, hexagon[4]], + [hexagon[5], corner4, hexagon[0]], + ]; + return [ + cornerTriangles[0], + hexagonTriangles[0], + hexagonTriangles[1], + hexagonTriangles[2], + cornerTriangles[1], + cornerTriangles[2], + hexagonTriangles[3], + hexagonTriangles[4], + hexagonTriangles[5], + cornerTriangles[3], + ]; } export function createSquare(sideLength:number): Polygon { const max = sideLength - 1; - const square = new Polygon(); + const square:Polygon = []; square.push(new Point(0,0)); square.push(new Point(0,max)); square.push(new Point(max,max)); @@ -33,24 +43,24 @@ return square; } -export function createHexagonInSquare(sideLength:number): Polygon +function createHexagonInSquare(sideLength:number): Polygon { const max = sideLength - 1; - return new Polygon(...[ + return [ new Point(max, max/2), new Point(3 * max / 4, 0), new Point(max / 4, 0), new Point(0, max / 2), new Point(max/4, max), new Point(3*max/4, max), - ]); + ]; } export function createTriangle(sideLength:number): Polygon { - return new Polygon( + return [ new Point(sideLength/2, 0), new Point(0,sideLength), new Point(sideLength, sideLength), - ); + ]; } diff --git a/public/index.html b/public/index.html index 5288ba7..3f2f32c 100644 --- a/public/index.html +++ b/public/index.html @@ -39,12 +39,8 @@

Inward spiral

-

Regular

-
-
-
-

Randomized

-
+

Square

+

Triangle

diff --git a/src/BaseShapes.ts b/src/BaseShapes.ts index dacf94c..e89d649 100644 --- a/src/BaseShapes.ts +++ b/src/BaseShapes.ts @@ -6,26 +6,36 @@ const center = new Point(max / 2, max / 2); const hexagon = createHexagonInSquare(max); const lines = zip(hexagon, hexagon, 1); - const base = lines.map(([a, b]) => { - return new Polygon(...[ - a, b, center, - ]); - }); + const hexagonTriangles = lines.map(([a, b]) => [a, b, center]); + const corner1 = new Point(max, 0); const corner2 = new Point(0, 0); const corner3 = new Point(0, max); const corner4 = new Point(max, max); - base.push(new Polygon(...[hexagon[0], corner1, hexagon[1]])); - base.push(new Polygon(...[hexagon[2], corner2, hexagon[3]])); - base.push(new Polygon(...[hexagon[3], corner3, hexagon[4]])); - base.push(new Polygon(...[hexagon[5], corner4, hexagon[0]])); - return base; + const cornerTriangles = [ + [hexagon[0], corner1, hexagon[1]], + [hexagon[2], corner2, hexagon[3]], + [hexagon[3], corner3, hexagon[4]], + [hexagon[5], corner4, hexagon[0]], + ]; + return [ + cornerTriangles[0], + hexagonTriangles[0], + hexagonTriangles[1], + hexagonTriangles[2], + cornerTriangles[1], + cornerTriangles[2], + hexagonTriangles[3], + hexagonTriangles[4], + hexagonTriangles[5], + cornerTriangles[3], + ]; } export function createSquare(sideLength:number): Polygon { const max = sideLength - 1; - const square = new Polygon(); + const square:Polygon = []; square.push(new Point(0,0)); square.push(new Point(0,max)); square.push(new Point(max,max)); @@ -33,24 +43,24 @@ return square; } -export function createHexagonInSquare(sideLength:number): Polygon +function createHexagonInSquare(sideLength:number): Polygon { const max = sideLength - 1; - return new Polygon(...[ + return [ new Point(max, max/2), new Point(3 * max / 4, 0), new Point(max / 4, 0), new Point(0, max / 2), new Point(max/4, max), new Point(3*max/4, max), - ]); + ]; } export function createTriangle(sideLength:number): Polygon { - return new Polygon( + return [ new Point(sideLength/2, 0), new Point(0,sideLength), new Point(sideLength, sideLength), - ); + ]; } diff --git a/src/Polygon.ts b/src/Polygon.ts index 85430f6..3e5a6b4 100644 --- a/src/Polygon.ts +++ b/src/Polygon.ts @@ -6,17 +6,4 @@ ) {} } -export class Polygon extends Array -{ - public translate(x:number, y:number): Polygon - { - const newCorners = new Polygon(); - - for(const corner of this) - { - newCorners.push(new Point(corner.x + x, corner.y + y)); - } - - return newCorners; - } -} +export type Polygon = Point[]; diff --git a/public/index.html b/public/index.html index 5288ba7..3f2f32c 100644 --- a/public/index.html +++ b/public/index.html @@ -39,12 +39,8 @@

Inward spiral

-

Regular

-
-
-
-

Randomized

-
+

Square

+

Triangle

diff --git a/src/BaseShapes.ts b/src/BaseShapes.ts index dacf94c..e89d649 100644 --- a/src/BaseShapes.ts +++ b/src/BaseShapes.ts @@ -6,26 +6,36 @@ const center = new Point(max / 2, max / 2); const hexagon = createHexagonInSquare(max); const lines = zip(hexagon, hexagon, 1); - const base = lines.map(([a, b]) => { - return new Polygon(...[ - a, b, center, - ]); - }); + const hexagonTriangles = lines.map(([a, b]) => [a, b, center]); + const corner1 = new Point(max, 0); const corner2 = new Point(0, 0); const corner3 = new Point(0, max); const corner4 = new Point(max, max); - base.push(new Polygon(...[hexagon[0], corner1, hexagon[1]])); - base.push(new Polygon(...[hexagon[2], corner2, hexagon[3]])); - base.push(new Polygon(...[hexagon[3], corner3, hexagon[4]])); - base.push(new Polygon(...[hexagon[5], corner4, hexagon[0]])); - return base; + const cornerTriangles = [ + [hexagon[0], corner1, hexagon[1]], + [hexagon[2], corner2, hexagon[3]], + [hexagon[3], corner3, hexagon[4]], + [hexagon[5], corner4, hexagon[0]], + ]; + return [ + cornerTriangles[0], + hexagonTriangles[0], + hexagonTriangles[1], + hexagonTriangles[2], + cornerTriangles[1], + cornerTriangles[2], + hexagonTriangles[3], + hexagonTriangles[4], + hexagonTriangles[5], + cornerTriangles[3], + ]; } export function createSquare(sideLength:number): Polygon { const max = sideLength - 1; - const square = new Polygon(); + const square:Polygon = []; square.push(new Point(0,0)); square.push(new Point(0,max)); square.push(new Point(max,max)); @@ -33,24 +43,24 @@ return square; } -export function createHexagonInSquare(sideLength:number): Polygon +function createHexagonInSquare(sideLength:number): Polygon { const max = sideLength - 1; - return new Polygon(...[ + return [ new Point(max, max/2), new Point(3 * max / 4, 0), new Point(max / 4, 0), new Point(0, max / 2), new Point(max/4, max), new Point(3*max/4, max), - ]); + ]; } export function createTriangle(sideLength:number): Polygon { - return new Polygon( + return [ new Point(sideLength/2, 0), new Point(0,sideLength), new Point(sideLength, sideLength), - ); + ]; } diff --git a/src/Polygon.ts b/src/Polygon.ts index 85430f6..3e5a6b4 100644 --- a/src/Polygon.ts +++ b/src/Polygon.ts @@ -6,17 +6,4 @@ ) {} } -export class Polygon extends Array -{ - public translate(x:number, y:number): Polygon - { - const newCorners = new Polygon(); - - for(const corner of this) - { - newCorners.push(new Point(corner.x + x, corner.y + y)); - } - - return newCorners; - } -} +export type Polygon = Point[]; diff --git a/src/SpiralFractal.ts b/src/SpiralFractal.ts index 3a3c477..1a42ef9 100644 --- a/src/SpiralFractal.ts +++ b/src/SpiralFractal.ts @@ -1,29 +1,72 @@ import { Polygon } from "./Polygon"; -import { zip, midPoint, randomizeScalar } from "./utils"; +import { zip, midPoint } from "./utils"; -function inwardSpiralStep(input:Polygon, scalar:number, randomizeShrinkRate:number): Polygon +function inwardSpiralStep( + {shrinkRate,firstShrinkRate}:ISpiralParameters, + input:Polygon, + index:number, +): Polygon { const lines = zip(input, input, 1); + let factor = shrinkRate; + if(index === 0) + { + factor = firstShrinkRate; + } const newPoints = lines.map(([a,b]) => - midPoint(a, b, randomizeScalar(scalar, randomizeShrinkRate, 0, 0.9))); - return new Polygon(...newPoints); + midPoint(a, b, factor)); + return newPoints; } -export function createInwardSpiral( - startShape:Polygon, - maxiterations:number, - shrinkRate:number, - randomizeShrinkRate:number, -): Polygon[] -{ - const cycle:Polygon[] = [startShape]; - let pNext:Polygon; - for(let i=0;i = (params:P, previous:T, index:number) => T; +export type FractalSequenceElement = (params:P) => T; + +export function Fractal( + startValue:T, + iterator:FractalIterator, + length:number, +): Array> +{ + const states = new Array(length+1); + states[0] = startValue; + const output:Array> = [ + () => startValue, + ]; + for(let i=0;i { + states[i+1] = iterator(p, states[i], i); + return states[i+1]; + }); + } + return output; +} + +export interface ISpiralParameters +{ + firstShrinkRate:number; + shrinkRate:number; +} + +export function InwardSpiral(startShape:Polygon, length:number): + Array> +{ + return Fractal( + startShape, + inwardSpiralStep, + length, + ); } diff --git a/public/index.html b/public/index.html index 5288ba7..3f2f32c 100644 --- a/public/index.html +++ b/public/index.html @@ -39,12 +39,8 @@

Inward spiral

-

Regular

-
-
-
-

Randomized

-
+

Square

+

Triangle

diff --git a/src/BaseShapes.ts b/src/BaseShapes.ts index dacf94c..e89d649 100644 --- a/src/BaseShapes.ts +++ b/src/BaseShapes.ts @@ -6,26 +6,36 @@ const center = new Point(max / 2, max / 2); const hexagon = createHexagonInSquare(max); const lines = zip(hexagon, hexagon, 1); - const base = lines.map(([a, b]) => { - return new Polygon(...[ - a, b, center, - ]); - }); + const hexagonTriangles = lines.map(([a, b]) => [a, b, center]); + const corner1 = new Point(max, 0); const corner2 = new Point(0, 0); const corner3 = new Point(0, max); const corner4 = new Point(max, max); - base.push(new Polygon(...[hexagon[0], corner1, hexagon[1]])); - base.push(new Polygon(...[hexagon[2], corner2, hexagon[3]])); - base.push(new Polygon(...[hexagon[3], corner3, hexagon[4]])); - base.push(new Polygon(...[hexagon[5], corner4, hexagon[0]])); - return base; + const cornerTriangles = [ + [hexagon[0], corner1, hexagon[1]], + [hexagon[2], corner2, hexagon[3]], + [hexagon[3], corner3, hexagon[4]], + [hexagon[5], corner4, hexagon[0]], + ]; + return [ + cornerTriangles[0], + hexagonTriangles[0], + hexagonTriangles[1], + hexagonTriangles[2], + cornerTriangles[1], + cornerTriangles[2], + hexagonTriangles[3], + hexagonTriangles[4], + hexagonTriangles[5], + cornerTriangles[3], + ]; } export function createSquare(sideLength:number): Polygon { const max = sideLength - 1; - const square = new Polygon(); + const square:Polygon = []; square.push(new Point(0,0)); square.push(new Point(0,max)); square.push(new Point(max,max)); @@ -33,24 +43,24 @@ return square; } -export function createHexagonInSquare(sideLength:number): Polygon +function createHexagonInSquare(sideLength:number): Polygon { const max = sideLength - 1; - return new Polygon(...[ + return [ new Point(max, max/2), new Point(3 * max / 4, 0), new Point(max / 4, 0), new Point(0, max / 2), new Point(max/4, max), new Point(3*max/4, max), - ]); + ]; } export function createTriangle(sideLength:number): Polygon { - return new Polygon( + return [ new Point(sideLength/2, 0), new Point(0,sideLength), new Point(sideLength, sideLength), - ); + ]; } diff --git a/src/Polygon.ts b/src/Polygon.ts index 85430f6..3e5a6b4 100644 --- a/src/Polygon.ts +++ b/src/Polygon.ts @@ -6,17 +6,4 @@ ) {} } -export class Polygon extends Array -{ - public translate(x:number, y:number): Polygon - { - const newCorners = new Polygon(); - - for(const corner of this) - { - newCorners.push(new Point(corner.x + x, corner.y + y)); - } - - return newCorners; - } -} +export type Polygon = Point[]; diff --git a/src/SpiralFractal.ts b/src/SpiralFractal.ts index 3a3c477..1a42ef9 100644 --- a/src/SpiralFractal.ts +++ b/src/SpiralFractal.ts @@ -1,29 +1,72 @@ import { Polygon } from "./Polygon"; -import { zip, midPoint, randomizeScalar } from "./utils"; +import { zip, midPoint } from "./utils"; -function inwardSpiralStep(input:Polygon, scalar:number, randomizeShrinkRate:number): Polygon +function inwardSpiralStep( + {shrinkRate,firstShrinkRate}:ISpiralParameters, + input:Polygon, + index:number, +): Polygon { const lines = zip(input, input, 1); + let factor = shrinkRate; + if(index === 0) + { + factor = firstShrinkRate; + } const newPoints = lines.map(([a,b]) => - midPoint(a, b, randomizeScalar(scalar, randomizeShrinkRate, 0, 0.9))); - return new Polygon(...newPoints); + midPoint(a, b, factor)); + return newPoints; } -export function createInwardSpiral( - startShape:Polygon, - maxiterations:number, - shrinkRate:number, - randomizeShrinkRate:number, -): Polygon[] -{ - const cycle:Polygon[] = [startShape]; - let pNext:Polygon; - for(let i=0;i = (params:P, previous:T, index:number) => T; +export type FractalSequenceElement = (params:P) => T; + +export function Fractal( + startValue:T, + iterator:FractalIterator, + length:number, +): Array> +{ + const states = new Array(length+1); + states[0] = startValue; + const output:Array> = [ + () => startValue, + ]; + for(let i=0;i { + states[i+1] = iterator(p, states[i], i); + return states[i+1]; + }); + } + return output; +} + +export interface ISpiralParameters +{ + firstShrinkRate:number; + shrinkRate:number; +} + +export function InwardSpiral(startShape:Polygon, length:number): + Array> +{ + return Fractal( + startShape, + inwardSpiralStep, + length, + ); } diff --git a/src/SvgRenderer.ts b/src/SvgRenderer.ts index e1aea8b..a03c236 100644 --- a/src/SvgRenderer.ts +++ b/src/SvgRenderer.ts @@ -1,23 +1,14 @@ import { Polygon } from "./Polygon"; +import { FractalSequenceElement, ISpiralParameters } from "./SpiralFractal"; +import { zip } from "./utils"; -function polygonToSvg( - svgRootElement:SVGSVGElement, - polygon:Polygon, +function createSvgPolygon( { fill }:{fill:string | null}, ): SVGPolygonElement { // Note: it is not possible to create any svg element without the namespace. const polygonSvg = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); polygonSvg.style.fill = fill; - for(const corner of polygon) - { - // This is the only way to create an SVGPoint: - const point = svgRootElement.createSVGPoint(); - point.x = corner.x; - point.y = corner.y; - polygonSvg.points.appendItem(point); - } - return polygonSvg; } @@ -28,28 +19,91 @@ }; } -export function fractalToSvg(fractal:Polygon[][], sideLength:number): SVGSVGElement +export function createSvgRootElement(sideLength:number): SVGSVGElement { - // Note: it is not possible to create any svg element without the namespace. - const el = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGSVGElement; - el.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, sideLength); - el.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, sideLength); - el.style.fill = "none"; - el.style.stroke = "purple"; - el.style.strokeWidth = "1"; + // Note: it is not possible to create any svg element without the namespace. + const el = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGSVGElement; + el.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, sideLength); + el.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, sideLength); + el.style.fill = "none"; + el.style.stroke = "purple"; + el.style.strokeWidth = "1"; + return el; +} - let colorStrength = 0; +export function createBasePolygons(length:number): SVGPolygonElement[] +{ const initialColorStrength = 20; - for(const segment of fractal) + let colorStrength = initialColorStrength; + const polygons:SVGPolygonElement[] = []; + for(let i=0;i, + ) { } + + public update(params:ISpiralParameters): void + { + const newState = this.fn(params); + for(let i=0;i this.polygon.points.length) + { + // add additional points + for(let i=this.polygon.points.length; i>, + ) { + if(this.polygons.length !== sequence.length) + { + throw new Error("SVGPolygons and sequence functions have a different count."); + } + + this.elements = + zip(polygons, sequence, 0) + .map(([p,s]) => new SvgSpiralPolygon(svgRootElement, p, s)); + } + + public update(params:ISpiralParameters): void + { + this.elements.forEach(e => e.update(params)); + } } diff --git a/public/index.html b/public/index.html index 5288ba7..3f2f32c 100644 --- a/public/index.html +++ b/public/index.html @@ -39,12 +39,8 @@

Inward spiral

-

Regular

-
-
-
-

Randomized

-
+

Square

+

Triangle

diff --git a/src/BaseShapes.ts b/src/BaseShapes.ts index dacf94c..e89d649 100644 --- a/src/BaseShapes.ts +++ b/src/BaseShapes.ts @@ -6,26 +6,36 @@ const center = new Point(max / 2, max / 2); const hexagon = createHexagonInSquare(max); const lines = zip(hexagon, hexagon, 1); - const base = lines.map(([a, b]) => { - return new Polygon(...[ - a, b, center, - ]); - }); + const hexagonTriangles = lines.map(([a, b]) => [a, b, center]); + const corner1 = new Point(max, 0); const corner2 = new Point(0, 0); const corner3 = new Point(0, max); const corner4 = new Point(max, max); - base.push(new Polygon(...[hexagon[0], corner1, hexagon[1]])); - base.push(new Polygon(...[hexagon[2], corner2, hexagon[3]])); - base.push(new Polygon(...[hexagon[3], corner3, hexagon[4]])); - base.push(new Polygon(...[hexagon[5], corner4, hexagon[0]])); - return base; + const cornerTriangles = [ + [hexagon[0], corner1, hexagon[1]], + [hexagon[2], corner2, hexagon[3]], + [hexagon[3], corner3, hexagon[4]], + [hexagon[5], corner4, hexagon[0]], + ]; + return [ + cornerTriangles[0], + hexagonTriangles[0], + hexagonTriangles[1], + hexagonTriangles[2], + cornerTriangles[1], + cornerTriangles[2], + hexagonTriangles[3], + hexagonTriangles[4], + hexagonTriangles[5], + cornerTriangles[3], + ]; } export function createSquare(sideLength:number): Polygon { const max = sideLength - 1; - const square = new Polygon(); + const square:Polygon = []; square.push(new Point(0,0)); square.push(new Point(0,max)); square.push(new Point(max,max)); @@ -33,24 +43,24 @@ return square; } -export function createHexagonInSquare(sideLength:number): Polygon +function createHexagonInSquare(sideLength:number): Polygon { const max = sideLength - 1; - return new Polygon(...[ + return [ new Point(max, max/2), new Point(3 * max / 4, 0), new Point(max / 4, 0), new Point(0, max / 2), new Point(max/4, max), new Point(3*max/4, max), - ]); + ]; } export function createTriangle(sideLength:number): Polygon { - return new Polygon( + return [ new Point(sideLength/2, 0), new Point(0,sideLength), new Point(sideLength, sideLength), - ); + ]; } diff --git a/src/Polygon.ts b/src/Polygon.ts index 85430f6..3e5a6b4 100644 --- a/src/Polygon.ts +++ b/src/Polygon.ts @@ -6,17 +6,4 @@ ) {} } -export class Polygon extends Array -{ - public translate(x:number, y:number): Polygon - { - const newCorners = new Polygon(); - - for(const corner of this) - { - newCorners.push(new Point(corner.x + x, corner.y + y)); - } - - return newCorners; - } -} +export type Polygon = Point[]; diff --git a/src/SpiralFractal.ts b/src/SpiralFractal.ts index 3a3c477..1a42ef9 100644 --- a/src/SpiralFractal.ts +++ b/src/SpiralFractal.ts @@ -1,29 +1,72 @@ import { Polygon } from "./Polygon"; -import { zip, midPoint, randomizeScalar } from "./utils"; +import { zip, midPoint } from "./utils"; -function inwardSpiralStep(input:Polygon, scalar:number, randomizeShrinkRate:number): Polygon +function inwardSpiralStep( + {shrinkRate,firstShrinkRate}:ISpiralParameters, + input:Polygon, + index:number, +): Polygon { const lines = zip(input, input, 1); + let factor = shrinkRate; + if(index === 0) + { + factor = firstShrinkRate; + } const newPoints = lines.map(([a,b]) => - midPoint(a, b, randomizeScalar(scalar, randomizeShrinkRate, 0, 0.9))); - return new Polygon(...newPoints); + midPoint(a, b, factor)); + return newPoints; } -export function createInwardSpiral( - startShape:Polygon, - maxiterations:number, - shrinkRate:number, - randomizeShrinkRate:number, -): Polygon[] -{ - const cycle:Polygon[] = [startShape]; - let pNext:Polygon; - for(let i=0;i = (params:P, previous:T, index:number) => T; +export type FractalSequenceElement = (params:P) => T; + +export function Fractal( + startValue:T, + iterator:FractalIterator, + length:number, +): Array> +{ + const states = new Array(length+1); + states[0] = startValue; + const output:Array> = [ + () => startValue, + ]; + for(let i=0;i { + states[i+1] = iterator(p, states[i], i); + return states[i+1]; + }); + } + return output; +} + +export interface ISpiralParameters +{ + firstShrinkRate:number; + shrinkRate:number; +} + +export function InwardSpiral(startShape:Polygon, length:number): + Array> +{ + return Fractal( + startShape, + inwardSpiralStep, + length, + ); } diff --git a/src/SvgRenderer.ts b/src/SvgRenderer.ts index e1aea8b..a03c236 100644 --- a/src/SvgRenderer.ts +++ b/src/SvgRenderer.ts @@ -1,23 +1,14 @@ import { Polygon } from "./Polygon"; +import { FractalSequenceElement, ISpiralParameters } from "./SpiralFractal"; +import { zip } from "./utils"; -function polygonToSvg( - svgRootElement:SVGSVGElement, - polygon:Polygon, +function createSvgPolygon( { fill }:{fill:string | null}, ): SVGPolygonElement { // Note: it is not possible to create any svg element without the namespace. const polygonSvg = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); polygonSvg.style.fill = fill; - for(const corner of polygon) - { - // This is the only way to create an SVGPoint: - const point = svgRootElement.createSVGPoint(); - point.x = corner.x; - point.y = corner.y; - polygonSvg.points.appendItem(point); - } - return polygonSvg; } @@ -28,28 +19,91 @@ }; } -export function fractalToSvg(fractal:Polygon[][], sideLength:number): SVGSVGElement +export function createSvgRootElement(sideLength:number): SVGSVGElement { - // Note: it is not possible to create any svg element without the namespace. - const el = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGSVGElement; - el.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, sideLength); - el.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, sideLength); - el.style.fill = "none"; - el.style.stroke = "purple"; - el.style.strokeWidth = "1"; + // Note: it is not possible to create any svg element without the namespace. + const el = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGSVGElement; + el.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, sideLength); + el.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, sideLength); + el.style.fill = "none"; + el.style.stroke = "purple"; + el.style.strokeWidth = "1"; + return el; +} - let colorStrength = 0; +export function createBasePolygons(length:number): SVGPolygonElement[] +{ const initialColorStrength = 20; - for(const segment of fractal) + let colorStrength = initialColorStrength; + const polygons:SVGPolygonElement[] = []; + for(let i=0;i, + ) { } + + public update(params:ISpiralParameters): void + { + const newState = this.fn(params); + for(let i=0;i this.polygon.points.length) + { + // add additional points + for(let i=this.polygon.points.length; i>, + ) { + if(this.polygons.length !== sequence.length) + { + throw new Error("SVGPolygons and sequence functions have a different count."); + } + + this.elements = + zip(polygons, sequence, 0) + .map(([p,s]) => new SvgSpiralPolygon(svgRootElement, p, s)); + } + + public update(params:ISpiralParameters): void + { + this.elements.forEach(e => e.update(params)); + } } diff --git a/src/index.ts b/src/index.ts index 2120815..210a87e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,33 +1,87 @@ -import { createInwardSpiral } from "./SpiralFractal"; +import { InwardSpiral, FractalSequenceElement, ISpiralParameters } from "./SpiralFractal"; import { createSquare, createTriangle, createHexagonalBase } from "./BaseShapes"; -import { fractalToSvg } from "./SvgRenderer"; +import { createSvgRootElement, createBasePolygons, SvgSpiralBody } from "./SvgRenderer"; +import { Polygon } from "./Polygon"; + +function makeSpirals( + sideLength:number, + iterations:number, + startShapes:Polygon[], +) +{ + const root = createSvgRootElement(sideLength); + const segments:SvgSpiralBody[] = []; + for (const startShape of startShapes) + { + const spiral = InwardSpiral(startShape, iterations); + const svgPolygons = createBasePolygons(spiral.length); + svgPolygons.forEach(p => root.appendChild(p)); + segments.push(new SvgSpiralBody(root, svgPolygons, spiral)); + } + + return segments; +} + +function createAnimationStep( + shrinkRate:number, + svgSpiral:SvgSpiralBody, + speed:number = 0.0025, +) +{ + const maxTime = shrinkRate; + let time = 0; + const animationStep = () => { + svgSpiral.update({shrinkRate, firstShrinkRate:time}); + time = (time + speed) % maxTime; + if(time < 0) + { + time += maxTime; + } + }; + return animationStep; +} + +function animatedSpiralFractal( + sideLength:number, + iterations:number, + shrinkRate:number, + startShapes:Polygon[], + htmlDivId:string, +) +{ + const animationSpeed = 0.005; + const svgSpirals = makeSpirals(sideLength, iterations, startShapes); + const divSpiral = document.getElementById(htmlDivId) as HTMLDivElement; + divSpiral.appendChild(svgSpirals[0].svgRootElement); + svgSpirals.forEach(s => s.update({shrinkRate, firstShrinkRate:shrinkRate})); + + return svgSpirals.map((s,i) => createAnimationStep(shrinkRate, s, ((2*((i+1)%2))-1) * animationSpeed)); +} function pageInit(): void { const sideLength = 400; const iterations = 50; const shrinkRate = 0.1; - const randomizeShrinkRate = 0.3; const square = createSquare(sideLength); - - const fractal = [createInwardSpiral(square, iterations, shrinkRate, 0)]; - const divSpiral = document.getElementById("spiral") as HTMLDivElement; - divSpiral.appendChild(fractalToSvg(fractal, sideLength)); - - const randomizedFractal = [createInwardSpiral(square, iterations, shrinkRate, randomizeShrinkRate)]; - const divRandomizedSpiral = document.getElementById("randomizedSpiral") as HTMLDivElement; - divRandomizedSpiral.appendChild(fractalToSvg(randomizedFractal, sideLength)); + const squareAnimation = animatedSpiralFractal(sideLength, iterations, shrinkRate, [square], "square"); const triangle = createTriangle(sideLength); - const triangleFractal = [createInwardSpiral(triangle, iterations, 1-0.06, 0)]; - const divTriangle = document.getElementById("triangle") as HTMLDivElement; - divTriangle.appendChild(fractalToSvg(triangleFractal, sideLength)); + const triangleAnimation = animatedSpiralFractal(sideLength, iterations, shrinkRate, [triangle], "triangle"); const hexagonalBase = createHexagonalBase(sideLength); - const hexagonalFractal = hexagonalBase.map(h => createInwardSpiral(h, iterations, 0.1, 0)); - const divHexagonal = document.getElementById("hexagonal") as HTMLDivElement; - divHexagonal.appendChild(fractalToSvg(hexagonalFractal, sideLength)); + const hexagonalAnimations = + animatedSpiralFractal(sideLength, iterations, shrinkRate, hexagonalBase, "hexagonal"); + + const animation = () => { + squareAnimation.forEach(a => a()); + triangleAnimation.forEach(a => a()); + hexagonalAnimations.forEach(a => a()); + requestAnimationFrame(animation); + }; + requestAnimationFrame(animation); + } document.addEventListener("DOMContentLoaded", pageInit);