diff options
Diffstat (limited to 'html/simple-shape/index.html')
-rw-r--r-- | html/simple-shape/index.html | 352 |
1 files changed, 214 insertions, 138 deletions
diff --git a/html/simple-shape/index.html b/html/simple-shape/index.html index 75b1f6b..4e4fa49 100644 --- a/html/simple-shape/index.html +++ b/html/simple-shape/index.html @@ -55,33 +55,137 @@ </div> <script> /** + * 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. - * The dimensions are calculated based on standard US Letter size (8.5x11 inches) at 300 DPI. */ const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); - canvas.width = 2550; // 8.5in * 300dpi - canvas.height = 3300; // 11in * 300dpi + canvas.width = CONFIG.canvas.width; + canvas.height = CONFIG.canvas.height; /** - * Utility Functions - * A collection of pure functions for randomization, following the functional programming paradigm. - * These provide a consistent interface for generating random values across the application. + * Utility Functions Module + * Pure functions for common operations + * @namespace Utils */ - const random = (min, max) => Math.random() * (max - min) + min; - const randomInt = (min, max) => Math.floor(random(min, max)); - const randomChoice = arr => arr[Math.floor(Math.random() * arr.length)]; + 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 - * Implements the Factory pattern to create basic geometric shapes. - * Each factory method is a pure function that takes position and size parameters. - * The factory uses method chaining with the Canvas API for drawing operations. - * @namespace + * @namespace Shapes */ - const shapes = { + const Shapes = { /** * Creates a circle with optional fill * @param {number} x - Center X coordinate @@ -121,20 +225,34 @@ /** * Pattern Generator System - * Implements the Strategy pattern for pattern generation. - * Uses higher-order functions to create composable pattern generators. - * Each pattern is a pure function that can be transformed and combined. - * - * @function generatePattern - * @returns {Function} A pattern generation function that takes x, y, and size parameters + * @namespace Patterns */ - const generatePattern = () => { + 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 pattern strategies - * Each strategy is a pure function implementing a specific pattern algorithm + * Collection of base pattern implementations * @type {Array<Function>} */ - const types = [ + types: [ /** * Grid-based pattern strategy * Demonstrates use of nested loops for regular grid generation @@ -147,7 +265,7 @@ for(let i = 0; i < 3; i++) { for(let j = 0; j < 3; j++) { if((i + j) % 2 === 0) { // Checkerboard pattern - shapes.circle( + Shapes.circle( x + spacing/2 + i * spacing, y + spacing/2 + j * spacing, spacing/2 @@ -162,7 +280,7 @@ 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); + Shapes.square(x + offset, y + offset, squareSize); } }, @@ -172,14 +290,14 @@ const radius = size/4; // Center circle - shapes.circle(x + center, y + center, size/6); + 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); + Shapes.circle(petalX, petalY, size/6); } }, @@ -187,7 +305,7 @@ (x, y, size) => { const triSize = size/3; for(let i = 0; i < 3; i++) { - shapes.triangle( + Shapes.triangle( x + i * triSize, y + (i % 2) * triSize/2, triSize @@ -200,7 +318,7 @@ const gridSize = size/2; for(let i = 0; i < 2; i++) { for(let j = 0; j < 2; j++) { - shapes.square( + Shapes.square( x + i * gridSize + size/8, y + j * gridSize + size/8, gridSize * 0.75 @@ -215,13 +333,13 @@ for(let i = 0; i < 2; i++) { for(let j = 0; j < 2; j++) { if((i + j) % 2 === 0) { - shapes.circle( + Shapes.circle( x + spacing/2 + i * spacing, y + spacing/2 + j * spacing, spacing/3 ); } else { - shapes.square( + Shapes.square( x + i * spacing + spacing/6, y + j * spacing + spacing/6, spacing * 2/3 @@ -235,11 +353,11 @@ (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); + 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); + Shapes.line(x, y, x + size, y + size); + Shapes.line(x + size, y, x, y + size); }, // Nested arcs @@ -278,7 +396,7 @@ (x, y, size) => { const center = size/2; for(let i = 1; i <= 3; i++) { - shapes.circle( + Shapes.circle( x + center, y + center, size, @@ -330,118 +448,76 @@ ctx.stroke(); } } - ]; - - /** - * Pattern Generator Factory - * Creates a new pattern generator with transformation capabilities - * Implements the Decorator pattern for adding rotation behavior - * @returns {Function} A function that generates a specific pattern with transformations - */ - return (x, y, size) => { - ctx.save(); - ctx.translate(x, y); - - // Rotation decorator - const rotation = Math.PI/2 * randomInt(0, 4); - if(rotation > 0) { - ctx.translate(size/2, size/2); - ctx.rotate(rotation); - ctx.translate(-size/2, -size/2); - } - - randomChoice(types)(0, 0, size); - ctx.restore(); - }; + ] }; /** - * Pattern Collection - * Creates a pool of pattern generators using the Factory pattern - * Limits the number of patterns to ensure visual coherence through repetition - * @type {Array<Function>} - */ - const patterns = Array(10).fill(null).map(generatePattern); - - /** * Layout System - * Implements a responsive grid layout system with gutters and margins - * Uses the Composite pattern to build complex layouts from simple components + * 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 }; + }, - /** - * Fisher-Yates shuffle implementation - * Used for randomizing pattern order while maintaining uniform distribution - * @param {Array} arr - Array to shuffle - * @returns {Array} New shuffled array - */ - const shuffle = arr => [...arr].sort(() => Math.random() - 0.5); + /** + * 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); + }); + }, - /** - * Row Composition Function - * Implements the Composite pattern for building rows of patterns - * Handles spacing and layout calculations for individual patterns - * @param {number} xOffset - Starting X position for the row - * @param {number} y - Y position for the row - * @param {number} size - Size of each pattern cell - */ - const drawPatternRow = (xOffset, y, size) => { - const gutter = size * 0.1; // 10% of pattern size for gutter - const patternWithGutter = size * 0.8; // 80% of space for actual pattern - - const shuffledPatterns = shuffle(patterns); - for(let i = 0; i < 5; i++) { - const xPos = xOffset + i * size + gutter; - const yPos = y + gutter; - shuffledPatterns[i](xPos, yPos, patternWithGutter); + /** + * 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); + }); } }; - /** - * Grid Layout Manager - * Implements the Facade pattern to simplify complex layout operations - * Handles margin calculations, row spacing, and overall composition - * Uses asymmetric margins for better visual balance - */ - const drawGrid = () => { - ctx.clearRect(0, 0, canvas.width, canvas.height); - - ctx.strokeStyle = '#000'; - ctx.lineWidth = 4; - - const patternSize = 400; - const numRows = 4; - const topMargin = 100; // Reduced from 300 - const bottomMargin = 300; // Keep original bottom margin - - // Calculate available space with asymmetric margins - const availableHeight = canvas.height - (topMargin + bottomMargin); - - // Calculate row spacing with increased gaps - const totalPatternHeight = numRows * patternSize; - const totalGapHeight = availableHeight - totalPatternHeight; - const rowGap = totalGapHeight / (numRows - 1); - - // Calculate horizontal centering - const totalWidth = patternSize * 5; - const xOffset = (canvas.width - totalWidth) / 2; - - // Draw each row - for(let i = 0; i < numRows; i++) { - const y = topMargin + i * (patternSize + rowGap); - drawPatternRow(xOffset, y, patternSize); - } - }; + // Generate pattern instances + const patternGenerators = Utils.range(0, 10) + .map(() => Patterns.createGenerator( + Utils.randomChoice(Patterns.types) + )); - // Initialize the system - drawGrid(); - - /** - * Event Handler - * Implements the Observer pattern for user interaction - * Regenerates the entire pattern grid on demand - */ - canvas.addEventListener('click', drawGrid); + // Initialize and set up interaction + Layout.drawGrid(); + canvas.addEventListener('click', Layout.drawGrid); </script> </body> </html> \ No newline at end of file |