Created
December 5, 2025 07:43
-
-
Save MagnusThor/d668e857ff429962fac04606bab2be69 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class SierpinskiTriangle { | |
| static get inputProperties() { | |
| return [ | |
| '--sierpinski-iterations', | |
| '--zoom-factor', | |
| '--fractal-opacity', | |
| '--rotation-angle' | |
| ]; | |
| } | |
| paint(ctx, size, props) { | |
| const maxIterations = parseInt(props.get('--sierpinski-iterations').toString()) || 12; | |
| const zoom = parseFloat(props.get('--zoom-factor').toString()) || 1.0; | |
| const opacity = parseFloat(props.get('--fractal-opacity').toString()) || 0.5; | |
| const rotationDegrees = parseFloat(props.get('--rotation-angle').toString()) || 0; | |
| const rotationRadians = rotationDegrees * Math.PI / 180; | |
| // --- 2. Context Setup --- | |
| ctx.globalAlpha = opacity; | |
| const zoom_center_x = size.width / 2; | |
| const zoom_center_y = size.height / 2; | |
| // --- 3. Apply Canvas Transformation (Rotation) --- | |
| ctx.save(); | |
| ctx.translate(zoom_center_x, zoom_center_y); | |
| ctx.rotate(rotationRadians); | |
| ctx.translate(-zoom_center_x, -zoom_center_y); | |
| // --- 4. Infinite Tunneling Logic --- | |
| const log2Zoom = Math.log2(zoom); | |
| // Scale factor cycles from 1.0 up to 2.0 (for seamless repetition) | |
| const scaleFactor = Math.pow(2, log2Zoom % 1); | |
| // --- 5. Define Base Triangle --- | |
| // Use a base size larger than the element to ensure full viewport coverage | |
| const maxDim = Math.max(size.width, size.height); | |
| const sideLength = maxDim * 1.5; | |
| const h = sideLength * Math.sqrt(3) / 2; | |
| const p1_base = { x: zoom_center_x, y: zoom_center_y - h / 2 }; | |
| const p2_base = { x: zoom_center_x - sideLength / 2, y: zoom_center_y + h / 2 }; | |
| const p3_base = { x: zoom_center_x + sideLength / 2, y: zoom_center_y + h / 2 }; | |
| // --- 6. Apply Zoom Scaling --- | |
| const p1_final = this.transformPoint(p1_base, scaleFactor, zoom_center_x, zoom_center_y); | |
| const p2_final = this.transformPoint(p2_base, scaleFactor, zoom_center_x, zoom_center_y); | |
| const p3_final = this.transformPoint(p3_base, scaleFactor, zoom_center_x, zoom_center_y); | |
| // --- 7. Start Recursion (and Color by Depth) --- | |
| this.drawTriangle(ctx, p1_final, p2_final, p3_final, maxIterations, maxIterations); | |
| ctx.restore(); // Restore context to remove rotation | |
| } | |
| // Helper for geometric scaling around a center point (cx, cy) | |
| transformPoint(p, scale, cx, cy) { | |
| return { | |
| x: cx + (p.x - cx) * scale, | |
| y: cy + (p.y - cy) * scale | |
| }; | |
| } | |
| // Recursive function with color-by-depth logic | |
| drawTriangle(ctx, pA, pB, pC, level, maxLevel) { | |
| if (level === 0) { | |
| // Base case: Draw the filled triangle | |
| // Calculate HSL color based on depth (maxLevel - level) | |
| // Creates a gradient effect from outer layers (maxLevel) to inner layers (0) | |
| const depth = maxLevel - level; | |
| const hue = 240 + depth * (60 / maxLevel); // Color shift (Blue to Purple) | |
| const lightness = 20 + depth * (30 / maxLevel); // Intensity shift (Darker to Lighter) | |
| ctx.fillStyle = `hsl(${hue}, 100%, ${lightness}%)`; | |
| ctx.beginPath(); | |
| ctx.moveTo(pA.x, pA.y); | |
| ctx.lineTo(pB.x, pB.y); | |
| ctx.lineTo(pC.x, pC.y); | |
| ctx.closePath(); | |
| ctx.fill(); | |
| } else { | |
| // Recursive step: Find the midpoints of the sides | |
| const pAB = { x: (pA.x + pB.x) / 2, y: (pA.y + pB.y) / 2 }; | |
| const pBC = { x: (pB.x + pC.x) / 2, y: (pB.y + pC.y) / 2 }; | |
| const pCA = { x: (pC.x + pA.x) / 2, y: (pC.y + pA.y) / 2 }; | |
| // Recursively call for the three smaller outer triangles | |
| this.drawTriangle(ctx, pA, pAB, pCA, level - 1, maxLevel); | |
| this.drawTriangle(ctx, pAB, pB, pBC, level - 1, maxLevel); | |
| this.drawTriangle(ctx, pCA, pBC, pC, level - 1, maxLevel); | |
| } | |
| } | |
| } | |
| registerPaint('sierpinski-triangle', SierpinskiTriangle); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment