about summary refs log tree commit diff stats
path: root/html/simple-shape/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/simple-shape/app.js')
-rw-r--r--html/simple-shape/app.js464
1 files changed, 464 insertions, 0 deletions
diff --git a/html/simple-shape/app.js b/html/simple-shape/app.js
new file mode 100644
index 0000000..b5260aa
--- /dev/null
+++ b/html/simple-shape/app.js
@@ -0,0 +1,464 @@
+/**
+ * Configuration object for application-wide settings
+ * Centralizes magic numbers and configuration values
+ * @constant {Object}
+ */
+const CONFIG = {
+    canvas: {
+        dpi: 300,
+        width: 8.5 * 300, // 8.5in at 300dpi
+        height: 11 * 300, // 11in at 300dpi
+    },
+    grid: {
+        rows: 4,
+        columns: 5,
+        topMargin: 100,
+        bottomMargin: 300,
+        patternSize: 400,
+        gutterRatio: 0.1, // 10% of pattern size
+        patternRatio: 0.8  // 80% of space for pattern
+    },
+    style: {
+        strokeWidth: 4,
+        strokeColor: '#000'
+    }
+};
+
+/**
+ * Canvas Setup and Configuration
+ * This section initializes a high-resolution canvas optimized for both screen display and printing.
+ */
+const canvas = document.getElementById('canvas');
+const ctx = canvas.getContext('2d');
+
+canvas.width = CONFIG.canvas.width;
+canvas.height = CONFIG.canvas.height;
+
+/**
+ * Utility Functions Module
+ * Pure functions for common operations
+ * @namespace Utils
+ */
+const Utils = {
+    /**
+     * Generates a random number within a range
+     * @param {number} min - Minimum value
+     * @param {number} max - Maximum value
+     * @returns {number}
+     */
+    random: (min, max) => Math.random() * (max - min) + min,
+    
+    /**
+     * Generates a random integer within a range
+     * @param {number} min - Minimum value
+     * @param {number} max - Maximum value
+     * @returns {number}
+     */
+    randomInt: (min, max) => Math.floor(Utils.random(min, max)),
+    
+    /**
+     * Randomly selects an item from an array
+     * @param {Array} arr - Source array
+     * @returns {*}
+     */
+    randomChoice: arr => arr[Math.floor(Math.random() * arr.length)],
+    
+    /**
+     * Fisher-Yates shuffle implementation
+     * @param {Array} arr - Array to shuffle
+     * @returns {Array} New shuffled array
+     */
+    shuffle: arr => [...arr].sort(() => Math.random() - 0.5),
+    
+    /**
+     * Creates a range array of numbers
+     * @param {number} start - Start value
+     * @param {number} end - End value
+     * @returns {Array<number>}
+     */
+    range: (start, end) => Array.from(
+        { length: end - start }, 
+        (_, i) => start + i
+    )
+};
+
+/**
+ * Drawing Context Manager
+ * Handles canvas context state management using functional composition
+ * @namespace ContextManager
+ */
+const ContextManager = {
+    /**
+     * Executes a drawing operation with saved context state
+     * @param {Function} drawFn - Drawing function to execute
+     * @returns {Function}
+     */
+    withContext: drawFn => (...args) => {
+        ctx.save();
+        drawFn(...args);
+        ctx.restore();
+    },
+
+    /**
+     * Applies a translation transformation
+     * @param {number} x - X translation
+     * @param {number} y - Y translation
+     * @returns {Function}
+     */
+    withTranslation: (x, y) => drawFn => (...args) => {
+        ctx.translate(x, y);
+        drawFn(...args);
+    },
+
+    /**
+     * Applies a rotation transformation around a point
+     * @param {number} angle - Rotation angle in radians
+     * @param {number} x - Center X coordinate
+     * @param {number} y - Center Y coordinate
+     * @returns {Function}
+     */
+    withRotation: (angle, x, y) => drawFn => (...args) => {
+        ctx.translate(x, y);
+        ctx.rotate(angle);
+        ctx.translate(-x, -y);
+        drawFn(...args);
+    }
+};
+
+/**
+ * Shape Factory Pattern
+ * @namespace Shapes
+ */
+const Shapes = {
+    /**
+     * Creates a circle with optional fill
+     * @param {number} x - Center X coordinate
+     * @param {number} y - Center Y coordinate
+     * @param {number} size - Reference size for radius calculation
+     * @param {Object} params - Optional parameters for customization
+     * @param {number} [params.radius] - Optional explicit radius
+     * @param {boolean} [params.fill] - Whether to fill the circle
+     */
+    circle: (x, y, size, params = {}) => {
+        const radius = params.radius || size/3;
+        ctx.beginPath();
+        ctx.arc(x, y, radius, 0, Math.PI * 2);
+        params.fill ? ctx.fill() : ctx.stroke();
+    },
+    
+    line: (x1, y1, x2, y2) => {
+        ctx.beginPath();
+        ctx.moveTo(x1, y1);
+        ctx.lineTo(x2, y2);
+        ctx.stroke();
+    },
+    
+    triangle: (x, y, size) => {
+        ctx.beginPath();
+        ctx.moveTo(x, y + size);
+        ctx.lineTo(x + size/2, y);
+        ctx.lineTo(x + size, y + size);
+        ctx.closePath();
+        ctx.stroke();
+    },
+
+    square: (x, y, size) => {
+        ctx.strokeRect(x, y, size, size);
+    }
+};
+
+/**
+ * Pattern Generator System
+ * @namespace Patterns
+ */
+const Patterns = {
+    /**
+     * Creates a pattern generator with transformation capabilities
+     * @param {Function} patternFn - Base pattern drawing function
+     * @returns {Function} Enhanced pattern generator
+     */
+    createGenerator: patternFn => {
+        return ContextManager.withContext((x, y, size) => {
+            const rotation = Math.PI/2 * Utils.randomInt(0, 4);
+            if (rotation > 0) {
+                ContextManager.withRotation(
+                    rotation,
+                    x + size/2,
+                    y + size/2
+                )(patternFn)(x, y, size);
+            } else {
+                patternFn(x, y, size);
+            }
+        });
+    },
+
+    /**
+     * Collection of base pattern implementations
+     * @type {Array<Function>}
+     */
+    types: [
+        /**
+         * Grid-based pattern strategy
+         * Demonstrates use of nested loops for regular grid generation
+         * @param {number} x - Starting X coordinate
+         * @param {number} y - Starting Y coordinate
+         * @param {number} size - Pattern size
+         */
+        (x, y, size) => {
+            const spacing = size/3;
+            for(let i = 0; i < 3; i++) {
+                for(let j = 0; j < 3; j++) {
+                    if((i + j) % 2 === 0) { // Checkerboard pattern
+                        Shapes.circle(
+                            x + spacing/2 + i * spacing,
+                            y + spacing/2 + j * spacing,
+                            spacing/2
+                        );
+                    }
+                }
+            }
+        },
+
+        // Nested squares
+        (x, y, size) => {
+            for(let i = 3; i > 0; i--) {
+                const offset = (3 - i) * size/6;
+                const squareSize = size - offset * 2;
+                Shapes.square(x + offset, y + offset, squareSize);
+            }
+        },
+
+        // Simple flower pattern
+        (x, y, size) => {
+            const center = size/2;
+            const radius = size/4;
+            
+            // Center circle
+            Shapes.circle(x + center, y + center, size/6);
+            
+            // Petals
+            for(let i = 0; i < 6; i++) {
+                const angle = (i / 6) * Math.PI * 2;
+                const petalX = x + center + Math.cos(angle) * radius;
+                const petalY = y + center + Math.sin(angle) * radius;
+                Shapes.circle(petalX, petalY, size/6);
+            }
+        },
+
+        // Triangles in a row
+        (x, y, size) => {
+            const triSize = size/3;
+            for(let i = 0; i < 3; i++) {
+                Shapes.triangle(
+                    x + i * triSize,
+                    y + (i % 2) * triSize/2,
+                    triSize
+                );
+            }
+        },
+
+        // Simple grid of squares
+        (x, y, size) => {
+            const gridSize = size/2;
+            for(let i = 0; i < 2; i++) {
+                for(let j = 0; j < 2; j++) {
+                    Shapes.square(
+                        x + i * gridSize + size/8,
+                        y + j * gridSize + size/8,
+                        gridSize * 0.75
+                    );
+                }
+            }
+        },
+
+        // Alternating circles and squares
+        (x, y, size) => {
+            const spacing = size/2;
+            for(let i = 0; i < 2; i++) {
+                for(let j = 0; j < 2; j++) {
+                    if((i + j) % 2 === 0) {
+                        Shapes.circle(
+                            x + spacing/2 + i * spacing,
+                            y + spacing/2 + j * spacing,
+                            spacing/3
+                        );
+                    } else {
+                        Shapes.square(
+                            x + i * spacing + spacing/6,
+                            y + j * spacing + spacing/6,
+                            spacing * 2/3
+                        );
+                    }
+                }
+            }
+        },
+
+        // Simple star pattern
+        (x, y, size) => {
+            const center = size/2;
+            // Horizontal and vertical lines
+            Shapes.line(x, y + center, x + size, y + center);
+            Shapes.line(x + center, y, x + center, y + size);
+            // Diagonal lines
+            Shapes.line(x, y, x + size, y + size);
+            Shapes.line(x + size, y, x, y + size);
+        },
+
+        // Nested arcs
+        (x, y, size) => {
+            const center = size/2;
+            for(let i = 1; i <= 4; i++) {
+                const radius = (size/2) * (i/4);
+                ctx.beginPath();
+                ctx.arc(x + center, y + center, radius, 0, Math.PI);
+                ctx.stroke();
+            }
+        },
+
+        // Quarter circles in corners
+        (x, y, size) => {
+            const radius = size/2;
+            // Top left
+            ctx.beginPath();
+            ctx.arc(x, y, radius, 0, Math.PI/2);
+            ctx.stroke();
+            // Top right
+            ctx.beginPath();
+            ctx.arc(x + size, y, radius, Math.PI/2, Math.PI);
+            ctx.stroke();
+            // Bottom right
+            ctx.beginPath();
+            ctx.arc(x + size, y + size, radius, Math.PI, Math.PI * 3/2);
+            ctx.stroke();
+            // Bottom left
+            ctx.beginPath();
+            ctx.arc(x, y + size, radius, Math.PI * 3/2, Math.PI * 2);
+            ctx.stroke();
+        },
+
+        // Concentric circles
+        (x, y, size) => {
+            const center = size/2;
+            for(let i = 1; i <= 3; i++) {
+                Shapes.circle(
+                    x + center,
+                    y + center,
+                    size,
+                    {radius: (size/2) * (i/3)}
+                );
+            }
+        },
+
+        // Nested diamonds
+        (x, y, size) => {
+            const center = size/2;
+            for(let i = 1; i <= 3; i++) {
+                const offset = (size/2) * (i/3);
+                ctx.beginPath();
+                ctx.moveTo(x + center, y + center - offset);
+                ctx.lineTo(x + center + offset, y + center);
+                ctx.lineTo(x + center, y + center + offset);
+                ctx.lineTo(x + center - offset, y + center);
+                ctx.closePath();
+                ctx.stroke();
+            }
+        },
+
+        // Radiating arcs
+        (x, y, size) => {
+            const center = size/2;
+            const radius = size/3;
+            for(let i = 0; i < 4; i++) {
+                const startAngle = (Math.PI/2) * i;
+                ctx.beginPath();
+                ctx.arc(x + center, y + center, radius, startAngle, startAngle + Math.PI/2);
+                ctx.stroke();
+            }
+        },
+
+        // Stacked semicircles
+        (x, y, size) => {
+            const width = size * 0.8;
+            for(let i = 0; i < 3; i++) {
+                ctx.beginPath();
+                ctx.arc(
+                    x + size/2,
+                    y + (size/3) * (i + 1),
+                    width/2,
+                    0,
+                    Math.PI,
+                    i % 2 === 0
+                );
+                ctx.stroke();
+            }
+        }
+    ]
+};
+
+/**
+ * Layout System
+ * Handles grid layout and composition
+ * @namespace Layout
+ */
+const Layout = {
+    /**
+     * Calculates layout metrics for the grid
+     * @returns {Object} Layout calculations
+     */
+    calculateMetrics: () => {
+        const availableHeight = CONFIG.canvas.height - 
+            (CONFIG.grid.topMargin + CONFIG.grid.bottomMargin);
+        
+        const totalPatternHeight = CONFIG.grid.rows * CONFIG.grid.patternSize;
+        const totalGapHeight = availableHeight - totalPatternHeight;
+        const rowGap = totalGapHeight / (CONFIG.grid.rows - 1);
+        
+        const totalWidth = CONFIG.grid.patternSize * CONFIG.grid.columns;
+        const xOffset = (CONFIG.canvas.width - totalWidth) / 2;
+        
+        return { rowGap, xOffset };
+    },
+
+    /**
+     * Draws a row of patterns
+     */
+    drawRow: (xOffset, y, size) => {
+        const gutter = size * CONFIG.grid.gutterRatio;
+        const patternSize = size * CONFIG.grid.patternRatio;
+        
+        const patterns = Utils.shuffle(patternGenerators);
+        Utils.range(0, CONFIG.grid.columns).forEach(i => {
+            const xPos = xOffset + i * size + gutter;
+            const yPos = y + gutter;
+            patterns[i](xPos, yPos, patternSize);
+        });
+    },
+
+    /**
+     * Draws the complete grid
+     */
+    drawGrid: () => {
+        ctx.clearRect(0, 0, CONFIG.canvas.width, CONFIG.canvas.height);
+        
+        ctx.strokeStyle = CONFIG.style.strokeColor;
+        ctx.lineWidth = CONFIG.style.strokeWidth;
+        
+        const { rowGap, xOffset } = Layout.calculateMetrics();
+        
+        Utils.range(0, CONFIG.grid.rows).forEach(i => {
+            const y = CONFIG.grid.topMargin + 
+                i * (CONFIG.grid.patternSize + rowGap);
+            Layout.drawRow(xOffset, y, CONFIG.grid.patternSize);
+        });
+    }
+};
+
+// Generate pattern instances
+const patternGenerators = Utils.range(0, 10)
+    .map(() => Patterns.createGenerator(
+        Utils.randomChoice(Patterns.types)
+    ));
+
+// Initialize and set up interaction
+Layout.drawGrid();
+canvas.addEventListener('click', Layout.drawGrid);
\ No newline at end of file