import { Polygon } from "./Polygon"; import { FractalSequenceElement, ISpiralParameters } from "./SpiralFractal"; import { zip } from "./utils"; 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; return polygonSvg; } function polygonStyle(strength:number) { return { fill: `rgb(0,0,${Math.floor(strength)})`, }; } 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"; return el; } export function createBasePolygons(length:number): SVGPolygonElement[] { const initialColorStrength = 20; let colorStrength = initialColorStrength; const polygons:SVGPolygonElement[] = []; for(let i=0;i<length;i++) { polygons.push(createSvgPolygon(polygonStyle(colorStrength))); colorStrength += (255-initialColorStrength)/length; } return polygons; } export class SvgSpiralPolygon { constructor( public svgRootElement:SVGSVGElement, public polygon:SVGPolygonElement, public fn:FractalSequenceElement<ISpiralParameters, Polygon>, ) { } public update(params:ISpiralParameters): void { const newState = this.fn(params); for(let i=0;i<newState.length && i<this.polygon.points.length;i++) { const point = this.polygon.points.getItem(i); point.x = newState[i].x; point.y = newState[i].y; } if(newState.length > this.polygon.points.length) { // add additional points for(let i=this.polygon.points.length; i<newState.length;i++) { const point = this.svgRootElement.createSVGPoint(); point.x = newState[i].x; point.y = newState[i].y; this.polygon.points.appendItem(point); } } else if(newState.length < this.polygon.points.length) { // remove points const oldPointCount = this.polygon.points.length; for(let i=newState.length;i<oldPointCount;i++) { this.polygon.points.removeItem(newState.length); } } } } export class SvgSpiralBody { private elements:SvgSpiralPolygon[]; constructor( public svgRootElement:SVGSVGElement, public polygons:SVGPolygonElement[], public sequence:Array<FractalSequenceElement<ISpiralParameters, Polygon>>, ) { 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)); } }