Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Created September 17, 2019 14:18
Show Gist options
  • Save mattdesl/eefda925e5388cb2d77dcaf59ba2f675 to your computer and use it in GitHub Desktop.
Save mattdesl/eefda925e5388cb2d77dcaf59ba2f675 to your computer and use it in GitHub Desktop.
random grid connections / Licensed under Polyform Non Commercial 1.0.0
// License: Polyform Non Commercial 1.0.0
// https://polyformproject.org/licenses/noncommercial/1.0.0/
const canvasSketch = require('canvas-sketch');
const Random = require('canvas-sketch-util/random');
const Color = require('canvas-sketch-util/color');
const { linspace, lerp } = require('canvas-sketch-util/math');
const risoColors = require('riso-colors').map(h => h.hex).filter(c => {
const hex = Color.parse(c).hex.toLowerCase();
return hex !== '#000000' && hex !== '#ffffff'
});
const paperColors = require('paper-colors').map(h => h.hex);
const settings = {
dimensions: [ 2048, 2048 ],
};
const sketch = ({ width, height, data }) => {
const {
seed = Random.getRandomSeed()
} = data;
const random = Random.createRandom(seed);
const {
background = 'tomato',
foreground = 'black',
thickness = 0.035,
// border = false,
border = Random.boolean(),
// thickness = random.range(0.025, 0.05),
margin = 0.1
} = data;
const lines = [];
const dim = Math.min(width, height);
const clipMargin = dim * 0.0;
const marginPx = dim * margin;
const repeats = Random.rangeFloor(1, 4);
for (let i = 0; i < repeats; i++) {
let grid = [];
const gridSize = random.rangeFloor(2, 4);
for (let y = 0; y < gridSize; y++) {
for (let x = 0; x < gridSize; x++) {
const u = x / (gridSize - 1);
const v = y / (gridSize - 1);
const px = lerp(marginPx, width - marginPx, u);
const py = lerp(marginPx, height - marginPx, v);
grid.push([ px, py ]);
}
}
while (grid.length > 2) {
const count = Random.range(2, 4);
const points = random.shuffle(grid).slice(0, count);
grid = grid.filter(p => !points.includes(p));
lines.push(points);
}
}
return ({ context }) => {
context.clearRect(0, 0, width, height);
context.fillStyle = background;
context.fillRect(clipMargin, clipMargin, width - clipMargin * 2, height - clipMargin * 2);
context.strokeStyle = foreground;
context.lineJoin = 'round';
context.lineCap = 'round';
context.lineWidth = dim * thickness;
if (border) {
context.strokeRect(marginPx, marginPx, width - marginPx * 2, height - marginPx * 2);
}
lines.forEach(line => {
context.beginPath();
line.forEach(([ x, y ]) => context.lineTo(x, y));
context.stroke();
});
};
};
function repeater (sketch, settings = {}) {
const gridSize = settings.gridSize || 4;
const dimensions = settings.dimensions || [ 2048, 2048 ];
let margin = 0;
if ('marginNormalized' in settings) {
margin = settings.marginNormalized * Math.min(dimensions[0], dimensions[1]);
} else if ('margin' in settings) {
margin = settings.margin;
}
const background = settings.background;
const marginStroke = settings.marginStroke;
settings = { ...settings, dimensions };
delete settings.margin;
delete settings.marginStroke;
delete settings.marginNormalized;
delete settings.gridSize;
delete settings.background;
canvasSketch(async ({ context, width, height }) => {
const sketches = [];
const cw = dimensions[0] / gridSize;
const ch = dimensions[1] / gridSize;
for (let y = 0; y < gridSize; y++) {
for (let x = 0; x < gridSize; x++) {
sketches.push({
sketch,
x,
y,
size: [ cw, ch ],
position: [ x * cw, y * ch ]
});
}
}
context.clearRect(0, 0, width, height);
if (background) {
context.fillStyle = background;
context.fillRect(0, 0, width, height);
}
// const hsl = Color.parse(background).hsl;
const mode = 'riso';
await sketches.reduce(async (promise, data, i) => {
await promise;
// const base = Random.pick(paperColors);
const base = Random.pick(risoColors);
const baseSat = 50 + Random.gaussian(0, 5);
const baseLight = 50 + Random.gaussian(0, 10);
let hsl = mode === 'riso'
? Color.parse(base).hsl
: [ Random.range(0, 360), baseSat + Random.gaussian(0, 5), baseLight + Random.gaussian(0, 5) ];
const [ h, s, l ] = hsl;
const foreground = Color.parse({
hsl
}).hex;
const colorStep = 1 / sketches.length;
// const hue = h + colorStep * i * 360 + 45;
const colorOffsetAmount = 45;
const colorOffsets = new Array(360 / colorOffsetAmount).fill(0).map((_, i) => colorOffsetAmount * i);
const offset = Random.pick(colorOffsets);
const sketchBackground = mode === 'riso'
? Random.pick(risoColors.filter(c => c !== base))
: Color.parse({
hsl: [ h + offset + Random.gaussian(0, 20), s + Random.gaussian(0, 5), l + Random.gaussian(0, 5) ]
// hsl: [ h + 90, s, l ]
// hsl: [ h + 180 + lerp(0, 360, colorStep * i), 50, 50 ]
// hsl: [ h, lerp(25, 75, colorStep * i), 50 ]
}).hex;
const manager = await canvasSketch(data.sketch, {
data: {
...data,
...settings.data,
background: sketchBackground,
foreground
},
parent: false,
dimensions: [ cw, ch ]
});
draw(context, width, height, manager, data);
manager.unload();
}, Promise.resolve());
if (background && marginStroke) {
context.strokeStyle = background;
const borderThickness = margin;
for (let y = 0; y <= gridSize; y++) {
const v = y / gridSize;
context.beginPath();
context.moveTo(0, v * height);
context.lineTo(width, v * height);
const borderScale = y === 0 || y === gridSize ? 2 : 1;
context.lineWidth = borderThickness * borderScale;
context.stroke();
}
for (let x = 0; x <= gridSize; x++) {
const u = x / gridSize;
context.beginPath();
context.moveTo(u * width, 0);
context.lineTo(u * width, height);
const borderScale = x === 0 || x === gridSize ? 2 : 1;
context.lineWidth = borderThickness * borderScale;
context.stroke();
}
}
}, settings);
function draw (context, width, height, manager, data) {
const curMargin = marginStroke ? 0 : margin;
context.drawImage(
manager.props.canvas,
data.position[0] + curMargin,
data.position[1] + curMargin,
data.size[0] - curMargin * 2,
data.size[1] - curMargin * 2
);
}
}
repeater(sketch, {
gridSize: Random.rangeFloor(2, 4),
background: 'white',
marginNormalized: 0.0,
marginStroke: false,
dimensions: [ 2048, 2048 ],
data: {
margin: Random.range(0.1, 0.3)
}
});
function getColors () {
const background = Color.parse({
hsl: [ Random.range(0, 360), 50, 50 ]
}).hex;
const [ h, s, l ] = Color.parse(background).hsl;
const foreground = Color.parse({
hsl: [ h + 360 / 2, s, l ]
}).hex;
return { background, foreground };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment