From e32005a732cf515d2441f4048e5cb8690619abe2 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Wed, 19 Aug 2020 22:31:28 +0200 Subject: [PATCH 01/12] Copied in library code --- .../src/components/app/app.jsx | 2 +- .../src/components/app/triangleMosaic.js | 631 ++++++++++++++++++ 2 files changed, 632 insertions(+), 1 deletion(-) create mode 100644 triangle-mosaic-demo/src/components/app/triangleMosaic.js diff --git a/triangle-mosaic-demo/src/components/app/app.jsx b/triangle-mosaic-demo/src/components/app/app.jsx index 313341b..8e13156 100644 --- a/triangle-mosaic-demo/src/components/app/app.jsx +++ b/triangle-mosaic-demo/src/components/app/app.jsx @@ -1,5 +1,4 @@ import React, {useEffect, useReducer, useRef, useState} from 'react' -import TriangleMosaic from 'triangle-mosaic' import Coloring from '../coloring/coloring' import ColoringPresets from '../coloringPresets/coloringPresets' @@ -8,6 +7,7 @@ import Variance from '../variance/variance' import {useDebounce} from './app.hooks' import {initialState, reducer} from './app.state' import {downloadSvg, getConfigFromState} from './app.utility' +import TriangleMosaic from './triangleMosaic' import './app.css' diff --git a/triangle-mosaic-demo/src/components/app/triangleMosaic.js b/triangle-mosaic-demo/src/components/app/triangleMosaic.js new file mode 100644 index 0000000..d81f704 --- /dev/null +++ b/triangle-mosaic-demo/src/components/app/triangleMosaic.js @@ -0,0 +1,631 @@ +// ---------------------------------------------------------------------------- +// Render + +const renderSvg = ({ + width, + height, + children +} = {}) => ` + + ${children} + +` + +const renderTriangle = ({ + edges, + color +}) => ` + +` + +// ---------------------------------------------------------------------------- +// Utility + +const movePoint = ({ + x, + y, + direction, + distance, + ...rest +}) => ({ + x: x + (distance * Math.cos(direction)), + y: y + (distance * Math.sin(direction)), + ...rest +}) + +const getDistance = ( + {x: x1, y: y1}, + {x: x2, y: y2} +) => ( + Math.sqrt( + ((x2 - x1) ** 2) + + ((y2 - y1) ** 2) + ) +) + +const hexToRgb = hex => { + const result = /^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(hex) + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16) + } : null +} + +const getRgbaColor = ({r, g, b, a}) => ( + (a === 1) + ? `rgb(${r}, ${g}, ${b})` + : `rgba(${r}, ${g}, ${b}, ${a})` +) + +const add = (a, b) => a + b + +const getAverage = (...numbers) => ( + numbers.reduce(add) / numbers.length +) + +const getTriangleCenter = ( + [ + {x: x1, y: y1}, + {x: x2, y: y2}, + {x: x3, y: y3} + ] +) => ({ + x: getAverage(x1, x2, x3), + y: getAverage(y1, y2, y3) +}) + +const modulo = (x, n) => (x % n + n) % n; + +const rotate = ({min, value, max}) => ( + min + (modulo(value - min, max - min)) +) + +const clamp = ({min, value, max}) => ( + Math.max(min, Math.min(max, value)) +) + +const getPerpendicularPoint = ({ + start: s, + end: e, + external: t +}) => { + const a = t.x - s.x + const b = t.y - s.y + const c = e.x - s.x + const d = e.y - s.y + const dot = (a * c) + (b * d) + const lengthSquared = (c ** 2) + (d ** 2) + const projectionLength = ( + (lengthSquared !== 0) + ? (dot / lengthSquared) + : -1 + ) + return { + x: s.x + (projectionLength * c), + y: s.y + (projectionLength * d) + } +} + +const getRatio = (start, end, target) => ( + (target - start) / (end - start) +) + +const hueToRgb = (p, q, t) => { + if (t < 0) { + t += 1 + } + if (t > 1) { + t -= 1 + } + if (t < 1/6) { + return p + (q - p) * 6 * t + } + if (t < 1/2) { + return q + } + if (t < 2/3) { + return p + (q - p) * (2/3 - t) * 6 + } + return p +} + +const hslaToRgba = ({h, s, l, a}) => { + let r, g, b + + if (s === 0) { + r = g = b = l + } + else { + const q = (l < 0.5) ? (l * (1 + s)) : (l + s - (l * s)) + const p = (2 * l) - q + r = hueToRgb(p, q, h + 1/3) + g = hueToRgb(p, q, h) + b = hueToRgb(p, q, h - 1/3) + } + + return { + r: r * 255, + g: g * 255, + b: b * 255, + a + } +} + +const rgbToHsl = ({r, g, b}) => { + r /= 255 + g /= 255 + b /= 255 + const max = Math.max(r, g, b) + const min = Math.min(r, g, b) + let h, s, l = (max + min) / 2 + + if (max === min) { + h = s = 0 + } + else { + const d = max - min + s = (l > 0.5) ? (d / (2 - max - min)) : (d / (max + min)) + + switch (max) { + case r: + h = ((g - b) / d) + ((g < b) ? 6 : 0) + break + case g: + h = ((b - r) / d) + 2 + break + case b: + h = ((r - g) / d) + 4 + break + } + + h /= 6 + } + + return {h, s, l} +} + +const adjustColor = ({color, adjustments}) => { + const {h, s, l} = rgbToHsl(color) + const adjustedHslaColor = { + h: rotate({ + min: 0, + max: 1, + value: h + (adjustments.hue * 0.15) + }), + s: clamp({ + min: 0, + max: 1, + value: s * (adjustments.saturation + 1) + }), + l: clamp({ + min: 0, + max: 1, + value: l * (adjustments.lightness + 1) + }), + a: clamp({ + min: 0, + max: 1, + value: 1 - adjustments.alpha + }) + } + return hslaToRgba(adjustedHslaColor) +} + +const getTriangleColorForGradient = ({coloring, center}) => { + const {start, end, stops, mode} = coloring + let ratio + + if (mode === 'linearGradient') { + const perpendicularPoint = getPerpendicularPoint({ + start, + end, + external: center + }) + + ratio = ( + (start.x !== end.x) + ? getRatio(start.x, end.x, perpendicularPoint.x) + : getRatio(start.y, end.y, perpendicularPoint.y) + ) || 0 + } + + if (mode === 'radialGradient') { + const gradientLength = getDistance(start, end) + const triangleDistance = getDistance(start, center) + + ratio = (triangleDistance / gradientLength) || 0 + } + + const exactMatch = stops.find(([location]) => ratio === location) + + if (ratio < 0) { + // Went negative overboard + return hexToRgb(stops[0][1]) + } + if (ratio > 1) { + // Went positive overboard + return hexToRgb(stops[stops.length - 1][1]) + } + if (exactMatch) { + // Hit a stop exactly + return hexToRgb(exactMatch[1]) + } + // Somewhere between two stops + const nextStopIndex = stops.findIndex(([location]) => location > ratio) + const previousStop = stops[nextStopIndex - 1] + const nextStop = stops[nextStopIndex] + const endRatio = getRatio(previousStop[0], nextStop[0], ratio) + const endColor = hexToRgb(nextStop[1]) + const startRatio = 1 - endRatio + const startColor = hexToRgb(previousStop[1]) + + return { + r: (startRatio * startColor.r) + (endRatio * endColor.r), + g: (startRatio * startColor.g) + (endRatio * endColor.g), + b: (startRatio * startColor.b) + (endRatio * endColor.b) + } +} + +const getTriangleColorForSpots = ({coloring, center}) => { + const {spots, spotIntensity} = coloring + + const getWeight = spot => ( + 1 / (getDistance(spot, center) ** (1 / (spot.intensity || spotIntensity || 0.5))) + ) + + const fullWeight = ( + spots + .map(getWeight) + .reduce(add, 0) + ) + + return ( + spots + .map(spot => ({ + // Get color components of the spot + color: hexToRgb(spot.color), + // Calculate how much this spot contributes to the color + factor: getWeight(spot) / fullWeight + })) + .map(({ + color: {r, g, b}, + factor + }) => ({ + // Calculate the color this spot contributes to the mix with + r: r * factor, + g: g * factor, + b: b * factor + })) + .reduce((accumulator, currentValue) => ({ + // Add color values of all spots (by color component) + r: accumulator.r + currentValue.r, + g: accumulator.g + currentValue.g, + b: accumulator.b + currentValue.b + }), {r: 0, g: 0, b: 0}) + ) +} + +const getTriangleColor = ({ + triangle, + colorFuzz, + colorDeviation, + coloring +}) => { + let color = {r: 0, g: 0, b: 0} + + if (coloring.mode === 'single') { + color = hexToRgb(coloring.color) + } + + const center = getTriangleCenter(triangle) + + if (['linearGradient', 'radialGradient'].includes(coloring.mode)) { + color = getTriangleColorForGradient({coloring, center}) + } + if (coloring.mode === 'spots') { + color = getTriangleColorForSpots({coloring, center}) + } + + const adjustedColor = adjustColor({ + color, + adjustments: { + hue: colorDeviation.hue * colorFuzz.hue, + saturation: colorDeviation.saturation * colorFuzz.saturation, + lightness: colorDeviation.lightness * colorFuzz.lightness, + alpha: colorDeviation.alpha * colorFuzz.alpha + } + }) + + return adjustedColor +} + +const getMaxVentureDistance = ({ + width, + height, + xResolution, + yResolution +}) => { + const horizontalDistance = height / yResolution + const verticalDistance = width / xResolution + const smallerDistance = Math.min(horizontalDistance, verticalDistance) + const maxVentureDistance = smallerDistance / 2 + return maxVentureDistance +} + +const getRandomTriangleColorDeviation = () => ({ + hue: (Math.random() * 2) - 1, + saturation: (Math.random() * 2) - 1, + lightness: (Math.random() * 2) - 1, + alpha: Math.random() +}) + +const getRandomGridPointOptions = () => ({ + direction: Math.random() * Math.PI * 2, + factor: Math.random(), + topTriangleColorDeviation: getRandomTriangleColorDeviation(), + bottomTriangleColorDeviation: getRandomTriangleColorDeviation() +}) + +// ---------------------------------------------------------------------------- +// Engine + +const getGrid = ({ + xResolution, + yResolution +}) => { + const numberOfRows = yResolution + 1 + const numberOfColumns = xResolution + 1 + + const gridPoints = [] + + for (let rowCounter = 0; rowCounter < numberOfRows; ++rowCounter) { + const gridPointsInRow = [] + for (let columnCounter = 0; columnCounter < numberOfColumns; ++columnCounter) { + gridPointsInRow.push({ + x: columnCounter, + y: rowCounter, + ...getRandomGridPointOptions() + }) + } + gridPoints.push(gridPointsInRow) + } + + return gridPoints +} + +const getTriangles = ({ + grid, + shapeFuzz, + colorFuzz, + coloring, + diagonals, + width, + height +}) => { + const numberOfRows = grid.length + const numberOfColumns = grid[0].length + const verticalDistance = height / (numberOfRows - 1) + const horizontalDistance = width / (numberOfColumns - 1) + + const triangles = [] + + // We'll take 4 grid points in one go to form 2 triangles + for (let rowCounter = 0; rowCounter < numberOfRows - 1; ++rowCounter) { + for (let columnCounter = 0; columnCounter < numberOfColumns - 1; ++columnCounter) { + const isFirstRow = (rowCounter === 0) + const isFirstColumn = (columnCounter === 0) + const isLastRow = (rowCounter === numberOfRows - 2) + const isLastColumn = (columnCounter === numberOfColumns - 2) + + const isPointStatic = pointPosition => { + if (pointPosition === 0) { + return isFirstRow || isFirstColumn + } + if (pointPosition === 1) { + return isFirstRow || isLastColumn + } + if (pointPosition === 2) { + return isFirstColumn || isLastRow + } + if (pointPosition === 3) { + return isLastRow || isLastColumn + } + } + + const points = [ + // 0----1 + // | / | + // | / | + // 2----3 + grid[rowCounter][columnCounter], + grid[rowCounter][columnCounter + 1], + grid[rowCounter + 1][columnCounter], + grid[rowCounter + 1][columnCounter + 1] + ].map((point, pointIndex) => ({ + ...point, + x: point.x * horizontalDistance, + y: point.y * verticalDistance, + direction: point.direction, + distance: ( + !isPointStatic(pointIndex) + ? (point.factor * shapeFuzz) + : 0 + ) + })).map(movePoint) + + const topTriangle = { + // Top triangle + // + // 0----1 + // | / + // | / + // 2 + edges: [points[0], points[1], points[2]], + color: getRgbaColor(getTriangleColor({ + triangle: [points[0], points[1], points[2]], + coloring, + colorFuzz, + colorDeviation: points[0].topTriangleColorDeviation + })) + } + + const bottomTriangle = { + // Bottom triangle + // + // 1 + // / | + // / | + // 2----3 + edges: [points[1], points[2], points[3]], + color: getRgbaColor(getTriangleColor({ + triangle: [points[1], points[2], points[3]], + coloring, + colorFuzz, + colorDeviation: points[0].bottomTriangleColorDeviation + })) + } + + triangles.push(topTriangle, bottomTriangle) + } + } + + return triangles +} + +// ---------------------------------------------------------------------------- +// State and API + +class TriangleMosaic { + constructor({ + width = 1280, + height = 720, + xResolution = 16, + yResolution = 9, + shapeFuzz = 0.65, + colorFuzz = { + hue: 0.1, + saturation: 0.1, + lightness: 0.1, + alpha: 0 + }, + coloring = { + mode: 'spots', + spots: [ + { + x: 0, + y: 0, + color: '#ffc107' + }, + { + x: 1280, + y: 0, + color: '#f44336' + }, + { + x: 640, + y: 720, + color: '#2196f3' + } + ] + } + } = {}) { + this.height = height + this.width = width + this.xResolution = xResolution + this.yResolution = yResolution + this.shapeFuzz = shapeFuzz + this.colorFuzz = colorFuzz + this.coloring = coloring + this.grid = getGrid({ + width, + height, + xResolution, + yResolution + }) + } + + render() { + const maxVentureDistance = getMaxVentureDistance({ + width: this.width, + height: this.height, + xResolution: this.xResolution, + yResolution: this.yResolution + }) + const triangles = getTriangles({ + grid: this.grid, + shapeFuzz: this.shapeFuzz * maxVentureDistance, + colorFuzz: this.colorFuzz, + coloring: this.coloring, + width: this.width, + height: this.height + }) + return renderSvg({ + width: this.width, + height: this.height, + children: triangles.map(renderTriangle).join('') + }) + } + + rehydrate({ + shapeFuzz, + colorFuzz, + coloring, + width, + height, + xResolution, + yResolution + } = {}) { + if (shapeFuzz !== undefined) { + this.shapeFuzz = shapeFuzz + } + if (colorFuzz && (colorFuzz.hue !== undefined)) { + this.colorFuzz.hue = colorFuzz.hue + } + if (colorFuzz && (colorFuzz.saturation !== undefined)) { + this.colorFuzz.saturation = colorFuzz.saturation + } + if (colorFuzz && (colorFuzz.lightness !== undefined)) { + this.colorFuzz.lightness = colorFuzz.lightness + } + if (colorFuzz && (colorFuzz.alpha !== undefined)) { + this.colorFuzz.alpha = colorFuzz.alpha + } + if (coloring !== undefined) { + this.coloring = coloring + } + if (width !== undefined) { + this.width = width + } + if (height !== undefined) { + this.height = height + } + if (xResolution !== undefined) { + this.xResolution = xResolution + } + if (yResolution !== undefined) { + this.yResolution = yResolution + } + if (xResolution !== undefined || yResolution !== undefined) { + this.grid = getGrid({ + width: this.width, + height: this.height, + xResolution: this.xResolution, + yResolution: this.yResolution + }) + } + return this.render() + } +} + +export default TriangleMosaic From 9cb65aa221d1b7df992eba1edc9777ca817dbfc3 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:54:55 +0200 Subject: [PATCH 02/12] Generated random diagonal settings for grid points --- triangle-mosaic-demo/src/components/app/triangleMosaic.js | 1 + 1 file changed, 1 insertion(+) diff --git a/triangle-mosaic-demo/src/components/app/triangleMosaic.js b/triangle-mosaic-demo/src/components/app/triangleMosaic.js index d81f704..c7c6111 100644 --- a/triangle-mosaic-demo/src/components/app/triangleMosaic.js +++ b/triangle-mosaic-demo/src/components/app/triangleMosaic.js @@ -373,6 +373,7 @@ const getRandomTriangleColorDeviation = () => ({ const getRandomGridPointOptions = () => ({ direction: Math.random() * Math.PI * 2, factor: Math.random(), + diagonal: Math.round(Math.random()), topTriangleColorDeviation: getRandomTriangleColorDeviation(), bottomTriangleColorDeviation: getRandomTriangleColorDeviation() }) From 55f1b7413d2c46838256363bcb6dc9e4e62d3482 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:56:26 +0200 Subject: [PATCH 03/12] Rendering diagonal based on new option --- .../src/components/app/triangleMosaic.js | 94 +++++++++++++++---- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/triangle-mosaic-demo/src/components/app/triangleMosaic.js b/triangle-mosaic-demo/src/components/app/triangleMosaic.js index c7c6111..113abfc 100644 --- a/triangle-mosaic-demo/src/components/app/triangleMosaic.js +++ b/triangle-mosaic-demo/src/components/app/triangleMosaic.js @@ -445,10 +445,12 @@ const getTriangles = ({ } const points = [ - // 0----1 - // | / | - // | / | - // 2----3 + // + // 0----1 0----1 + // | / | or | \ | + // | / | | \ | + // 2----3 2----3 + // grid[rowCounter][columnCounter], grid[rowCounter][columnCounter + 1], grid[rowCounter + 1][columnCounter], @@ -465,35 +467,82 @@ const getTriangles = ({ ) })).map(movePoint) + const { + topTriangleColorDeviation, + bottomTriangleColorDeviation, + diagonal: pointDiagonal + } = points[0] + + const diagonal = (() => { + if (diagonals === 'nw-se') { + return 0 + } + if (diagonals === 'ne-sw') { + return 1 + } + if (diagonals === 'alternating') { + return ( + ( + ((rowCounter % 2) === 0) && ((columnCounter % 2) === 1) + || ((rowCounter % 2) === 1) && ((columnCounter % 2) === 0) + ) + ? 0 + : 1 + ) + } + if (diagonals === 'random') { + return pointDiagonal + } + return 0 + })() + const topTriangle = { // Top triangle // - // 0----1 - // | / - // | / - // 2 - edges: [points[0], points[1], points[2]], + // 0----1 0----1 + // | / or \ | + // | / \ | + // 2 3 + // + edges: ( + (diagonal === 0) + ? [points[0], points[1], points[2]] + : [points[0], points[1], points[3]] + ), color: getRgbaColor(getTriangleColor({ - triangle: [points[0], points[1], points[2]], + triangle: ( + (diagonal === 0) + ? [points[0], points[1], points[2]] + : [points[0], points[1], points[3]] + ), coloring, colorFuzz, - colorDeviation: points[0].topTriangleColorDeviation + colorDeviation: topTriangleColorDeviation })) } const bottomTriangle = { // Bottom triangle // - // 1 - // / | - // / | - // 2----3 - edges: [points[1], points[2], points[3]], + // 1 0 + // / | or | \ + // / | | \ + // 2----3 2----3 + // + edges: ( + (diagonal === 0) + ? [points[1], points[3], points[2]] + : [points[0], points[3], points[2]] + ), color: getRgbaColor(getTriangleColor({ - triangle: [points[1], points[2], points[3]], + triangle: ( + (diagonal === 0) + ? [points[1], points[3], points[2]] + : [points[0], points[3], points[2]] + ), coloring, colorFuzz, - colorDeviation: points[0].bottomTriangleColorDeviation + colorDeviation: bottomTriangleColorDeviation })) } @@ -520,6 +569,7 @@ class TriangleMosaic { lightness: 0.1, alpha: 0 }, + diagonals = 'ne-sw', coloring = { mode: 'spots', spots: [ @@ -547,6 +597,7 @@ class TriangleMosaic { this.yResolution = yResolution this.shapeFuzz = shapeFuzz this.colorFuzz = colorFuzz + this.diagonals = diagonals this.coloring = coloring this.grid = getGrid({ width, @@ -567,6 +618,7 @@ class TriangleMosaic { grid: this.grid, shapeFuzz: this.shapeFuzz * maxVentureDistance, colorFuzz: this.colorFuzz, + diagonals: this.diagonals, coloring: this.coloring, width: this.width, height: this.height @@ -584,8 +636,9 @@ class TriangleMosaic { coloring, width, height, + diagonals, xResolution, - yResolution + yResolution, } = {}) { if (shapeFuzz !== undefined) { this.shapeFuzz = shapeFuzz @@ -611,6 +664,9 @@ class TriangleMosaic { if (height !== undefined) { this.height = height } + if (diagonals !== undefined) { + this.diagonals = diagonals + } if (xResolution !== undefined) { this.xResolution = xResolution } From 8662b42dd84bb83caaf287bfe9619ef544048c75 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:56:53 +0200 Subject: [PATCH 04/12] Added state management for diagonals on demo page --- triangle-mosaic-demo/src/components/app/app.state.js | 7 +++++++ triangle-mosaic-demo/src/components/app/app.utility.js | 2 ++ 2 files changed, 9 insertions(+) diff --git a/triangle-mosaic-demo/src/components/app/app.state.js b/triangle-mosaic-demo/src/components/app/app.state.js index 8d2986f..9535724 100644 --- a/triangle-mosaic-demo/src/components/app/app.state.js +++ b/triangle-mosaic-demo/src/components/app/app.state.js @@ -32,6 +32,7 @@ const initialState = { lightness: 0.1, alpha: 0 }, + diagonals: 'nw-se', coloringMode: 'single', coloringSingle: { color: '#ffc107' @@ -107,6 +108,7 @@ const actions = { updateSaturationFuzz: 'updateSaturationFuzz', updateLightnessFuzz: 'updateLightnessFuzz', updateAlphaFuzz: 'updateAlphaFuzz', + updateDiagonals: 'updateDiagonals', updateColoringMode: 'updateColoringMode', updateSingleColor: 'updateSingleColor', addSpot: 'addSpot', @@ -185,6 +187,11 @@ const reducer = (state, action) => { alpha: action.alphaFuzz } } + case actions.updateDiagonals: + return { + ...state, + diagonals: action.diagonals + } case actions.updateColoringMode: return { ...state, diff --git a/triangle-mosaic-demo/src/components/app/app.utility.js b/triangle-mosaic-demo/src/components/app/app.utility.js index 7f44c28..387982b 100644 --- a/triangle-mosaic-demo/src/components/app/app.utility.js +++ b/triangle-mosaic-demo/src/components/app/app.utility.js @@ -31,6 +31,7 @@ const getConfigFromState = ({ yResolution, shapeFuzz, colorFuzz, + diagonals, coloringMode, coloringSingle, coloringGradient, @@ -45,6 +46,7 @@ const getConfigFromState = ({ }), shapeFuzz, colorFuzz, + diagonals, coloring: { mode: coloringMode, ...((coloringMode === 'single') && coloringSingle), From 97f7046fbe53aefa0e21947d692d3329d40a6e93 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:57:16 +0200 Subject: [PATCH 05/12] Added input for diagonals --- .../src/components/app/app.jsx | 1 + .../src/components/variance/variance.jsx | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/triangle-mosaic-demo/src/components/app/app.jsx b/triangle-mosaic-demo/src/components/app/app.jsx index 8e13156..142d2cd 100644 --- a/triangle-mosaic-demo/src/components/app/app.jsx +++ b/triangle-mosaic-demo/src/components/app/app.jsx @@ -44,6 +44,7 @@ const App = () => { (
@@ -106,11 +107,29 @@ const Variance = ({ step={0.01} /> +
+ + {/* eslint-disable-next-line jsx-a11y/no-onchange */} + +
) Variance.propTypes = { shapeFuzz: number, + diagonals: oneOf(['nw-se', 'ne-sw', 'alternating', 'random']), // eslint-disable-next-line react/forbid-prop-types colorFuzz: object, dispatch: func From cae243e7868f37254b5c82515a5158c839d59eaf Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:58:22 +0200 Subject: [PATCH 06/12] Applied library updates to actual source file --- triangle-mosaic/triangle-mosaic.js | 96 ++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/triangle-mosaic/triangle-mosaic.js b/triangle-mosaic/triangle-mosaic.js index 120c2cc..113abfc 100644 --- a/triangle-mosaic/triangle-mosaic.js +++ b/triangle-mosaic/triangle-mosaic.js @@ -373,6 +373,7 @@ const getRandomTriangleColorDeviation = () => ({ const getRandomGridPointOptions = () => ({ direction: Math.random() * Math.PI * 2, factor: Math.random(), + diagonal: Math.round(Math.random()), topTriangleColorDeviation: getRandomTriangleColorDeviation(), bottomTriangleColorDeviation: getRandomTriangleColorDeviation() }) @@ -409,6 +410,7 @@ const getTriangles = ({ shapeFuzz, colorFuzz, coloring, + diagonals, width, height }) => { @@ -443,10 +445,12 @@ const getTriangles = ({ } const points = [ - // 0----1 - // | / | - // | / | - // 2----3 + // + // 0----1 0----1 + // | / | or | \ | + // | / | | \ | + // 2----3 2----3 + // grid[rowCounter][columnCounter], grid[rowCounter][columnCounter + 1], grid[rowCounter + 1][columnCounter], @@ -463,35 +467,82 @@ const getTriangles = ({ ) })).map(movePoint) + const { + topTriangleColorDeviation, + bottomTriangleColorDeviation, + diagonal: pointDiagonal + } = points[0] + + const diagonal = (() => { + if (diagonals === 'nw-se') { + return 0 + } + if (diagonals === 'ne-sw') { + return 1 + } + if (diagonals === 'alternating') { + return ( + ( + ((rowCounter % 2) === 0) && ((columnCounter % 2) === 1) + || ((rowCounter % 2) === 1) && ((columnCounter % 2) === 0) + ) + ? 0 + : 1 + ) + } + if (diagonals === 'random') { + return pointDiagonal + } + return 0 + })() + const topTriangle = { // Top triangle // - // 0----1 - // | / - // | / - // 2 - edges: [points[0], points[1], points[2]], + // 0----1 0----1 + // | / or \ | + // | / \ | + // 2 3 + // + edges: ( + (diagonal === 0) + ? [points[0], points[1], points[2]] + : [points[0], points[1], points[3]] + ), color: getRgbaColor(getTriangleColor({ - triangle: [points[0], points[1], points[2]], + triangle: ( + (diagonal === 0) + ? [points[0], points[1], points[2]] + : [points[0], points[1], points[3]] + ), coloring, colorFuzz, - colorDeviation: points[0].topTriangleColorDeviation + colorDeviation: topTriangleColorDeviation })) } const bottomTriangle = { // Bottom triangle // - // 1 - // / | - // / | - // 2----3 - edges: [points[1], points[2], points[3]], + // 1 0 + // / | or | \ + // / | | \ + // 2----3 2----3 + // + edges: ( + (diagonal === 0) + ? [points[1], points[3], points[2]] + : [points[0], points[3], points[2]] + ), color: getRgbaColor(getTriangleColor({ - triangle: [points[1], points[2], points[3]], + triangle: ( + (diagonal === 0) + ? [points[1], points[3], points[2]] + : [points[0], points[3], points[2]] + ), coloring, colorFuzz, - colorDeviation: points[0].bottomTriangleColorDeviation + colorDeviation: bottomTriangleColorDeviation })) } @@ -518,6 +569,7 @@ class TriangleMosaic { lightness: 0.1, alpha: 0 }, + diagonals = 'ne-sw', coloring = { mode: 'spots', spots: [ @@ -545,6 +597,7 @@ class TriangleMosaic { this.yResolution = yResolution this.shapeFuzz = shapeFuzz this.colorFuzz = colorFuzz + this.diagonals = diagonals this.coloring = coloring this.grid = getGrid({ width, @@ -565,6 +618,7 @@ class TriangleMosaic { grid: this.grid, shapeFuzz: this.shapeFuzz * maxVentureDistance, colorFuzz: this.colorFuzz, + diagonals: this.diagonals, coloring: this.coloring, width: this.width, height: this.height @@ -582,8 +636,9 @@ class TriangleMosaic { coloring, width, height, + diagonals, xResolution, - yResolution + yResolution, } = {}) { if (shapeFuzz !== undefined) { this.shapeFuzz = shapeFuzz @@ -609,6 +664,9 @@ class TriangleMosaic { if (height !== undefined) { this.height = height } + if (diagonals !== undefined) { + this.diagonals = diagonals + } if (xResolution !== undefined) { this.xResolution = xResolution } From 067fb3524adacda36ae8ae3d148f6491bafbfad0 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 23:04:55 +0200 Subject: [PATCH 07/12] Updated change log --- changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index 9aa180b..a9fcca1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # Change log +## 1.1.0 + +Bugfix: Gradients do not fail anymore if start and end point have the same x coordinates. + +Bugfix: Hue fuzz/variance can now correctly cross the 0-360° mark. + +New feature: Introduced `diagonal` option. + + ## 1.0.1 Got rid of optional chaining operator as it was causing issues. From 5e24d971fa7f296c10777b174629e50bb53cc4f4 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 23:12:29 +0200 Subject: [PATCH 08/12] Updated readme with diagonals --- README.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e57ac38..0d32824 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,14 @@ The options `xResolution` and `yResolution` are the only options which cannot be ### Variance -| Member name | Description | -|------------------------|----------------------------------------------------| -| `shapeFuzz` | Variance factor of grid points (0-1) | -| `colorFuzz.hue` | Variance factor of triangle color hue (0-1) | -| `colorFuzz.saturation` | Variance factor of triangle color saturation (0-1) | -| `colorFuzz.lightness` | Variance factor of triangle color lightness (0-1) | -| `colorFuzz.alpha` | Variance factor of triangle color alpha (0-1) | +| Member name | Description | +|------------------------|-------------------------------------------------------------| +| `shapeFuzz` | Variance factor of grid points (0-1) | +| `colorFuzz.hue` | Variance factor of triangle color hue (0-1) | +| `colorFuzz.saturation` | Variance factor of triangle color saturation (0-1) | +| `colorFuzz.lightness` | Variance factor of triangle color lightness (0-1) | +| `colorFuzz.alpha` | Variance factor of triangle color alpha (0-1) | +| `diagonals` | Either `'nw-se'`, `'ne-sw'`, `'alternating'`, or `'random'` | Variance options control how the triangles can deviate from their original shape and coloring. @@ -93,6 +94,11 @@ The option `shapeFuzz` controls how far the grid points may venture from their o Parameters that belong the object `colorFuzz` control how much the triangle colors may deviate from their original color. This can be controlled along all four axes of the HSLA color representation. +The option `diagonals` controls which diagonals of the squares of the grid are used: +- `'nw-se'` draws the diagonals from the top left corners to the bottom right corners (North-West - South-East) +- `'ne-sw'` draws the diagonals from the top right corners to the bottom left corners (North-East - South-West) +- `'alternating'` draws diagonals alternating, so that no two neighboring squares have the same diagonal +- `'random'` draws diagonals randomly For example: @@ -106,6 +112,7 @@ const options = { lightness: 0.1, alpha: 0 }, + diagonals: 'nw-se', ... } ``` @@ -264,6 +271,7 @@ let options = { lightness: 0.1, alpha: 0 }, + diagonals: 'nw-se', coloring: { mode: 'linearGradient', start: { @@ -326,4 +334,4 @@ console.log(myNewSvgCode) Triangle Mosaic is [licensed under MIT](./LICENSE). -Whatever you create with the library or web-based tool is yours and you may license it in any way you see fit. +The images you create with the library or web-based tool are yours and you may license them in any way you see fit. From 4503a05ea3b2bffea3bbf8ab8d29589aeb6d71bf Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 23:12:54 +0200 Subject: [PATCH 09/12] 1.1.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f77730d..a38f82d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { "name": "triangle-mosaic", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 1 } diff --git a/package.json b/package.json index 804ab51..8d3911a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "triangle-mosaic", - "version": "1.0.1", + "version": "1.1.0", "description": "Generate colorful triangle-based SVG patterns (like this point_down) with ease.", "main": "./triangle-mosaic/triangle-mosaic.js", "repository": { From e0a62d63f21f2bba152a16661a46c1c5229a37a1 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 23:24:22 +0200 Subject: [PATCH 10/12] Updated triangle-mosaic on demo page --- triangle-mosaic-demo/package-lock.json | 6 +++--- triangle-mosaic-demo/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/triangle-mosaic-demo/package-lock.json b/triangle-mosaic-demo/package-lock.json index 1296ac1..36b9179 100644 --- a/triangle-mosaic-demo/package-lock.json +++ b/triangle-mosaic-demo/package-lock.json @@ -17449,9 +17449,9 @@ } }, "triangle-mosaic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/triangle-mosaic/-/triangle-mosaic-1.0.1.tgz", - "integrity": "sha512-J6l90+MV4BeKrELc3EEepcANo88m6pQP24tZSXSt2+uvOgqRItE1o20hS/c1qFmVVvUqHRZm7FbaBemlzKR6VA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/triangle-mosaic/-/triangle-mosaic-1.1.0.tgz", + "integrity": "sha512-O9nWsawT73rARaPAshldRTeCG73WOdEXUx8dvU3wEDa+hD/3LDmXzntigNNyfYs0VK9uOPojoDyF0Vnz4hqCDQ==" }, "trim": { "version": "0.0.1", diff --git a/triangle-mosaic-demo/package.json b/triangle-mosaic-demo/package.json index 723f982..0b4f076 100644 --- a/triangle-mosaic-demo/package.json +++ b/triangle-mosaic-demo/package.json @@ -17,7 +17,7 @@ "react-scripts": "3.4.3", "stylelint": "^13.6.1", "stylelint-config-standard": "^20.0.0", - "triangle-mosaic": "^1.0.1" + "triangle-mosaic": "^1.1.0" }, "scripts": { "start": "react-scripts start", From 0cbdb089fdbc1d21a167882aabfb871c4b481e9d Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 23:26:41 +0200 Subject: [PATCH 11/12] Importing library from npm package --- .../src/components/app/app.jsx | 2 +- .../src/components/app/triangleMosaic.js | 688 ------------------ 2 files changed, 1 insertion(+), 689 deletions(-) delete mode 100644 triangle-mosaic-demo/src/components/app/triangleMosaic.js diff --git a/triangle-mosaic-demo/src/components/app/app.jsx b/triangle-mosaic-demo/src/components/app/app.jsx index 142d2cd..0f2b0eb 100644 --- a/triangle-mosaic-demo/src/components/app/app.jsx +++ b/triangle-mosaic-demo/src/components/app/app.jsx @@ -1,4 +1,5 @@ import React, {useEffect, useReducer, useRef, useState} from 'react' +import TriangleMosaic from 'triangle-mosaic' import Coloring from '../coloring/coloring' import ColoringPresets from '../coloringPresets/coloringPresets' @@ -7,7 +8,6 @@ import Variance from '../variance/variance' import {useDebounce} from './app.hooks' import {initialState, reducer} from './app.state' import {downloadSvg, getConfigFromState} from './app.utility' -import TriangleMosaic from './triangleMosaic' import './app.css' diff --git a/triangle-mosaic-demo/src/components/app/triangleMosaic.js b/triangle-mosaic-demo/src/components/app/triangleMosaic.js deleted file mode 100644 index 113abfc..0000000 --- a/triangle-mosaic-demo/src/components/app/triangleMosaic.js +++ /dev/null @@ -1,688 +0,0 @@ -// ---------------------------------------------------------------------------- -// Render - -const renderSvg = ({ - width, - height, - children -} = {}) => ` - - ${children} - -` - -const renderTriangle = ({ - edges, - color -}) => ` - -` - -// ---------------------------------------------------------------------------- -// Utility - -const movePoint = ({ - x, - y, - direction, - distance, - ...rest -}) => ({ - x: x + (distance * Math.cos(direction)), - y: y + (distance * Math.sin(direction)), - ...rest -}) - -const getDistance = ( - {x: x1, y: y1}, - {x: x2, y: y2} -) => ( - Math.sqrt( - ((x2 - x1) ** 2) + - ((y2 - y1) ** 2) - ) -) - -const hexToRgb = hex => { - const result = /^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(hex) - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null -} - -const getRgbaColor = ({r, g, b, a}) => ( - (a === 1) - ? `rgb(${r}, ${g}, ${b})` - : `rgba(${r}, ${g}, ${b}, ${a})` -) - -const add = (a, b) => a + b - -const getAverage = (...numbers) => ( - numbers.reduce(add) / numbers.length -) - -const getTriangleCenter = ( - [ - {x: x1, y: y1}, - {x: x2, y: y2}, - {x: x3, y: y3} - ] -) => ({ - x: getAverage(x1, x2, x3), - y: getAverage(y1, y2, y3) -}) - -const modulo = (x, n) => (x % n + n) % n; - -const rotate = ({min, value, max}) => ( - min + (modulo(value - min, max - min)) -) - -const clamp = ({min, value, max}) => ( - Math.max(min, Math.min(max, value)) -) - -const getPerpendicularPoint = ({ - start: s, - end: e, - external: t -}) => { - const a = t.x - s.x - const b = t.y - s.y - const c = e.x - s.x - const d = e.y - s.y - const dot = (a * c) + (b * d) - const lengthSquared = (c ** 2) + (d ** 2) - const projectionLength = ( - (lengthSquared !== 0) - ? (dot / lengthSquared) - : -1 - ) - return { - x: s.x + (projectionLength * c), - y: s.y + (projectionLength * d) - } -} - -const getRatio = (start, end, target) => ( - (target - start) / (end - start) -) - -const hueToRgb = (p, q, t) => { - if (t < 0) { - t += 1 - } - if (t > 1) { - t -= 1 - } - if (t < 1/6) { - return p + (q - p) * 6 * t - } - if (t < 1/2) { - return q - } - if (t < 2/3) { - return p + (q - p) * (2/3 - t) * 6 - } - return p -} - -const hslaToRgba = ({h, s, l, a}) => { - let r, g, b - - if (s === 0) { - r = g = b = l - } - else { - const q = (l < 0.5) ? (l * (1 + s)) : (l + s - (l * s)) - const p = (2 * l) - q - r = hueToRgb(p, q, h + 1/3) - g = hueToRgb(p, q, h) - b = hueToRgb(p, q, h - 1/3) - } - - return { - r: r * 255, - g: g * 255, - b: b * 255, - a - } -} - -const rgbToHsl = ({r, g, b}) => { - r /= 255 - g /= 255 - b /= 255 - const max = Math.max(r, g, b) - const min = Math.min(r, g, b) - let h, s, l = (max + min) / 2 - - if (max === min) { - h = s = 0 - } - else { - const d = max - min - s = (l > 0.5) ? (d / (2 - max - min)) : (d / (max + min)) - - switch (max) { - case r: - h = ((g - b) / d) + ((g < b) ? 6 : 0) - break - case g: - h = ((b - r) / d) + 2 - break - case b: - h = ((r - g) / d) + 4 - break - } - - h /= 6 - } - - return {h, s, l} -} - -const adjustColor = ({color, adjustments}) => { - const {h, s, l} = rgbToHsl(color) - const adjustedHslaColor = { - h: rotate({ - min: 0, - max: 1, - value: h + (adjustments.hue * 0.15) - }), - s: clamp({ - min: 0, - max: 1, - value: s * (adjustments.saturation + 1) - }), - l: clamp({ - min: 0, - max: 1, - value: l * (adjustments.lightness + 1) - }), - a: clamp({ - min: 0, - max: 1, - value: 1 - adjustments.alpha - }) - } - return hslaToRgba(adjustedHslaColor) -} - -const getTriangleColorForGradient = ({coloring, center}) => { - const {start, end, stops, mode} = coloring - let ratio - - if (mode === 'linearGradient') { - const perpendicularPoint = getPerpendicularPoint({ - start, - end, - external: center - }) - - ratio = ( - (start.x !== end.x) - ? getRatio(start.x, end.x, perpendicularPoint.x) - : getRatio(start.y, end.y, perpendicularPoint.y) - ) || 0 - } - - if (mode === 'radialGradient') { - const gradientLength = getDistance(start, end) - const triangleDistance = getDistance(start, center) - - ratio = (triangleDistance / gradientLength) || 0 - } - - const exactMatch = stops.find(([location]) => ratio === location) - - if (ratio < 0) { - // Went negative overboard - return hexToRgb(stops[0][1]) - } - if (ratio > 1) { - // Went positive overboard - return hexToRgb(stops[stops.length - 1][1]) - } - if (exactMatch) { - // Hit a stop exactly - return hexToRgb(exactMatch[1]) - } - // Somewhere between two stops - const nextStopIndex = stops.findIndex(([location]) => location > ratio) - const previousStop = stops[nextStopIndex - 1] - const nextStop = stops[nextStopIndex] - const endRatio = getRatio(previousStop[0], nextStop[0], ratio) - const endColor = hexToRgb(nextStop[1]) - const startRatio = 1 - endRatio - const startColor = hexToRgb(previousStop[1]) - - return { - r: (startRatio * startColor.r) + (endRatio * endColor.r), - g: (startRatio * startColor.g) + (endRatio * endColor.g), - b: (startRatio * startColor.b) + (endRatio * endColor.b) - } -} - -const getTriangleColorForSpots = ({coloring, center}) => { - const {spots, spotIntensity} = coloring - - const getWeight = spot => ( - 1 / (getDistance(spot, center) ** (1 / (spot.intensity || spotIntensity || 0.5))) - ) - - const fullWeight = ( - spots - .map(getWeight) - .reduce(add, 0) - ) - - return ( - spots - .map(spot => ({ - // Get color components of the spot - color: hexToRgb(spot.color), - // Calculate how much this spot contributes to the color - factor: getWeight(spot) / fullWeight - })) - .map(({ - color: {r, g, b}, - factor - }) => ({ - // Calculate the color this spot contributes to the mix with - r: r * factor, - g: g * factor, - b: b * factor - })) - .reduce((accumulator, currentValue) => ({ - // Add color values of all spots (by color component) - r: accumulator.r + currentValue.r, - g: accumulator.g + currentValue.g, - b: accumulator.b + currentValue.b - }), {r: 0, g: 0, b: 0}) - ) -} - -const getTriangleColor = ({ - triangle, - colorFuzz, - colorDeviation, - coloring -}) => { - let color = {r: 0, g: 0, b: 0} - - if (coloring.mode === 'single') { - color = hexToRgb(coloring.color) - } - - const center = getTriangleCenter(triangle) - - if (['linearGradient', 'radialGradient'].includes(coloring.mode)) { - color = getTriangleColorForGradient({coloring, center}) - } - if (coloring.mode === 'spots') { - color = getTriangleColorForSpots({coloring, center}) - } - - const adjustedColor = adjustColor({ - color, - adjustments: { - hue: colorDeviation.hue * colorFuzz.hue, - saturation: colorDeviation.saturation * colorFuzz.saturation, - lightness: colorDeviation.lightness * colorFuzz.lightness, - alpha: colorDeviation.alpha * colorFuzz.alpha - } - }) - - return adjustedColor -} - -const getMaxVentureDistance = ({ - width, - height, - xResolution, - yResolution -}) => { - const horizontalDistance = height / yResolution - const verticalDistance = width / xResolution - const smallerDistance = Math.min(horizontalDistance, verticalDistance) - const maxVentureDistance = smallerDistance / 2 - return maxVentureDistance -} - -const getRandomTriangleColorDeviation = () => ({ - hue: (Math.random() * 2) - 1, - saturation: (Math.random() * 2) - 1, - lightness: (Math.random() * 2) - 1, - alpha: Math.random() -}) - -const getRandomGridPointOptions = () => ({ - direction: Math.random() * Math.PI * 2, - factor: Math.random(), - diagonal: Math.round(Math.random()), - topTriangleColorDeviation: getRandomTriangleColorDeviation(), - bottomTriangleColorDeviation: getRandomTriangleColorDeviation() -}) - -// ---------------------------------------------------------------------------- -// Engine - -const getGrid = ({ - xResolution, - yResolution -}) => { - const numberOfRows = yResolution + 1 - const numberOfColumns = xResolution + 1 - - const gridPoints = [] - - for (let rowCounter = 0; rowCounter < numberOfRows; ++rowCounter) { - const gridPointsInRow = [] - for (let columnCounter = 0; columnCounter < numberOfColumns; ++columnCounter) { - gridPointsInRow.push({ - x: columnCounter, - y: rowCounter, - ...getRandomGridPointOptions() - }) - } - gridPoints.push(gridPointsInRow) - } - - return gridPoints -} - -const getTriangles = ({ - grid, - shapeFuzz, - colorFuzz, - coloring, - diagonals, - width, - height -}) => { - const numberOfRows = grid.length - const numberOfColumns = grid[0].length - const verticalDistance = height / (numberOfRows - 1) - const horizontalDistance = width / (numberOfColumns - 1) - - const triangles = [] - - // We'll take 4 grid points in one go to form 2 triangles - for (let rowCounter = 0; rowCounter < numberOfRows - 1; ++rowCounter) { - for (let columnCounter = 0; columnCounter < numberOfColumns - 1; ++columnCounter) { - const isFirstRow = (rowCounter === 0) - const isFirstColumn = (columnCounter === 0) - const isLastRow = (rowCounter === numberOfRows - 2) - const isLastColumn = (columnCounter === numberOfColumns - 2) - - const isPointStatic = pointPosition => { - if (pointPosition === 0) { - return isFirstRow || isFirstColumn - } - if (pointPosition === 1) { - return isFirstRow || isLastColumn - } - if (pointPosition === 2) { - return isFirstColumn || isLastRow - } - if (pointPosition === 3) { - return isLastRow || isLastColumn - } - } - - const points = [ - // - // 0----1 0----1 - // | / | or | \ | - // | / | | \ | - // 2----3 2----3 - // - grid[rowCounter][columnCounter], - grid[rowCounter][columnCounter + 1], - grid[rowCounter + 1][columnCounter], - grid[rowCounter + 1][columnCounter + 1] - ].map((point, pointIndex) => ({ - ...point, - x: point.x * horizontalDistance, - y: point.y * verticalDistance, - direction: point.direction, - distance: ( - !isPointStatic(pointIndex) - ? (point.factor * shapeFuzz) - : 0 - ) - })).map(movePoint) - - const { - topTriangleColorDeviation, - bottomTriangleColorDeviation, - diagonal: pointDiagonal - } = points[0] - - const diagonal = (() => { - if (diagonals === 'nw-se') { - return 0 - } - if (diagonals === 'ne-sw') { - return 1 - } - if (diagonals === 'alternating') { - return ( - ( - ((rowCounter % 2) === 0) && ((columnCounter % 2) === 1) - || ((rowCounter % 2) === 1) && ((columnCounter % 2) === 0) - ) - ? 0 - : 1 - ) - } - if (diagonals === 'random') { - return pointDiagonal - } - return 0 - })() - - const topTriangle = { - // Top triangle - // - // 0----1 0----1 - // | / or \ | - // | / \ | - // 2 3 - // - edges: ( - (diagonal === 0) - ? [points[0], points[1], points[2]] - : [points[0], points[1], points[3]] - ), - color: getRgbaColor(getTriangleColor({ - triangle: ( - (diagonal === 0) - ? [points[0], points[1], points[2]] - : [points[0], points[1], points[3]] - ), - coloring, - colorFuzz, - colorDeviation: topTriangleColorDeviation - })) - } - - const bottomTriangle = { - // Bottom triangle - // - // 1 0 - // / | or | \ - // / | | \ - // 2----3 2----3 - // - edges: ( - (diagonal === 0) - ? [points[1], points[3], points[2]] - : [points[0], points[3], points[2]] - ), - color: getRgbaColor(getTriangleColor({ - triangle: ( - (diagonal === 0) - ? [points[1], points[3], points[2]] - : [points[0], points[3], points[2]] - ), - coloring, - colorFuzz, - colorDeviation: bottomTriangleColorDeviation - })) - } - - triangles.push(topTriangle, bottomTriangle) - } - } - - return triangles -} - -// ---------------------------------------------------------------------------- -// State and API - -class TriangleMosaic { - constructor({ - width = 1280, - height = 720, - xResolution = 16, - yResolution = 9, - shapeFuzz = 0.65, - colorFuzz = { - hue: 0.1, - saturation: 0.1, - lightness: 0.1, - alpha: 0 - }, - diagonals = 'ne-sw', - coloring = { - mode: 'spots', - spots: [ - { - x: 0, - y: 0, - color: '#ffc107' - }, - { - x: 1280, - y: 0, - color: '#f44336' - }, - { - x: 640, - y: 720, - color: '#2196f3' - } - ] - } - } = {}) { - this.height = height - this.width = width - this.xResolution = xResolution - this.yResolution = yResolution - this.shapeFuzz = shapeFuzz - this.colorFuzz = colorFuzz - this.diagonals = diagonals - this.coloring = coloring - this.grid = getGrid({ - width, - height, - xResolution, - yResolution - }) - } - - render() { - const maxVentureDistance = getMaxVentureDistance({ - width: this.width, - height: this.height, - xResolution: this.xResolution, - yResolution: this.yResolution - }) - const triangles = getTriangles({ - grid: this.grid, - shapeFuzz: this.shapeFuzz * maxVentureDistance, - colorFuzz: this.colorFuzz, - diagonals: this.diagonals, - coloring: this.coloring, - width: this.width, - height: this.height - }) - return renderSvg({ - width: this.width, - height: this.height, - children: triangles.map(renderTriangle).join('') - }) - } - - rehydrate({ - shapeFuzz, - colorFuzz, - coloring, - width, - height, - diagonals, - xResolution, - yResolution, - } = {}) { - if (shapeFuzz !== undefined) { - this.shapeFuzz = shapeFuzz - } - if (colorFuzz && (colorFuzz.hue !== undefined)) { - this.colorFuzz.hue = colorFuzz.hue - } - if (colorFuzz && (colorFuzz.saturation !== undefined)) { - this.colorFuzz.saturation = colorFuzz.saturation - } - if (colorFuzz && (colorFuzz.lightness !== undefined)) { - this.colorFuzz.lightness = colorFuzz.lightness - } - if (colorFuzz && (colorFuzz.alpha !== undefined)) { - this.colorFuzz.alpha = colorFuzz.alpha - } - if (coloring !== undefined) { - this.coloring = coloring - } - if (width !== undefined) { - this.width = width - } - if (height !== undefined) { - this.height = height - } - if (diagonals !== undefined) { - this.diagonals = diagonals - } - if (xResolution !== undefined) { - this.xResolution = xResolution - } - if (yResolution !== undefined) { - this.yResolution = yResolution - } - if (xResolution !== undefined || yResolution !== undefined) { - this.grid = getGrid({ - width: this.width, - height: this.height, - xResolution: this.xResolution, - yResolution: this.yResolution - }) - } - return this.render() - } -} - -export default TriangleMosaic From b8be301103adc32d1cddcc6ed7a43469b88c3000 Mon Sep 17 00:00:00 2001 From: "Bence A. Toth" <5611525+bence-toth@users.noreply.github.com> Date: Thu, 20 Aug 2020 23:26:51 +0200 Subject: [PATCH 12/12] Updated footer --- .../src/components/app/app.css | 9 +++++++- .../src/components/app/app.jsx | 22 ++++++++++--------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/triangle-mosaic-demo/src/components/app/app.css b/triangle-mosaic-demo/src/components/app/app.css index 2a304f8..b96af95 100644 --- a/triangle-mosaic-demo/src/components/app/app.css +++ b/triangle-mosaic-demo/src/components/app/app.css @@ -26,10 +26,12 @@ #sidebar > footer { text-align: center; + margin-bottom: 2rem; } #sidebar > footer > .byLine { - font-size: 1.1rem; + margin-top: 1rem; + font-size: 0.8rem; } #sidebar > footer a { @@ -39,6 +41,11 @@ text-decoration: none; } +#sidebar > footer > .byLine a { + padding: 0; + font-size: 0.8rem; +} + #sidebar > footer a:hover, #sidebar > footer a:active { text-decoration: underline; diff --git a/triangle-mosaic-demo/src/components/app/app.jsx b/triangle-mosaic-demo/src/components/app/app.jsx index 0f2b0eb..de9b67c 100644 --- a/triangle-mosaic-demo/src/components/app/app.jsx +++ b/triangle-mosaic-demo/src/components/app/app.jsx @@ -61,36 +61,38 @@ const App = () => { />