Add confetti shape style

This commit is contained in:
Julian Fietkau 2024-09-12 14:56:49 +02:00
parent a072d608c0
commit 59c0d2d5cd

View File

@ -230,6 +230,10 @@ function compactPathSpec(oldPathSpec) {
if((step[0] == 'h' || step[0] == 'v') && step[0] == prev[0]) { if((step[0] == 'h' || step[0] == 'v') && step[0] == prev[0]) {
let distance = parseInt(prev.substring(1), 10) + parseInt(step.substring(1), 10); let distance = parseInt(prev.substring(1), 10) + parseInt(step.substring(1), 10);
newPathSpec[newPathSpec.length - 1] = step[0] + distance; newPathSpec[newPathSpec.length - 1] = step[0] + distance;
} else if(step[0] == 'm' && (prev[0] == 'm' || prev[0] == 'M')) {
let prevPos = prev.substring(1).split(' ').map(c => parseFloat(c));
let newDelta = step.substring(1).split(' ').map(c => parseFloat(c));
newPathSpec[newPathSpec.length - 1] = prev[0] + (prevPos[0] + newDelta[0]).toPrecision(3) + ' ' + (prevPos[1] + newDelta[1]).toPrecision(3);
} else { } else {
newPathSpec.push(step); newPathSpec.push(step);
} }
@ -243,8 +247,62 @@ function calculateTileStyleContour(bitmask, margin, style) {
if(!['dots', 'mosaic', 'confetti'].includes(style)) { if(!['dots', 'mosaic', 'confetti'].includes(style)) {
throw Error('Unsupported tiled render style: ' + style); throw Error('Unsupported tiled render style: ' + style);
} }
let confettiShapes = [
[ // circle
'M 0.5 0.1',
'A 0.4 0.4 0 0 1 0.5 0.9',
'A 0.4 0.4 0 0 1 0.5 0.1',
],
[ // rectangle
'M 0.1 0.15',
'L 0.9 0.15',
'L 0.9 0.85',
'L 0.1 0.85',
'L 0.1 0.15',
],
[ // star
'M 0.68 0.254',
'Q 1.21 0.284 0.786 0.58',
'Q 0.9275 1.0925 0.498 0.801',
'Q 0.0545 1.1 0.212 0.606',
'Q -0.208 0.269 0.312 0.259',
'Q 0.5 -0.25 0.68 0.254',
],
[ // heart
'M 0.5 0.3',
'C 1.2 -0.1 1 0.7 0.5 1',
'C 0 0.7 -0.2 -0.1 0.5 0.3',
],
[ // diamond
'M 0.5 0',
'Q 0.65 0.35 1 0.5',
'Q 0.65 0.65 0.5 1',
'Q 0.35 0.65 0 0.5',
'Q 0.35 0.35 0.5 0',
],
[ // shamrock
'M 0.45 0.45',
'A 0.23 0.23 0 1 1 0.55 0.45',
'A 0.23 0.23 0 1 1 0.54 0.48',
'L 0.7 0.95',
'L 0.3 0.95',
'L 0.46 0.48',
'A 0.23 0.23 0 1 1 0.45 0.45',
],
[ // lemon
'M 0.5 0',
'Q 1.5 0.5 0.5 1',
'Q -0.5 0.5 0.5 0',
],
];
let contour = new Contour(); let contour = new Contour();
let prng = new PRNG(1); let prng = new PRNG(1);
// Rotate a point around (0.5, 0.5) by angle in radians
let rotatePoint = (x, y, angle) => {
let rotatedX = 0.5 + (x - 0.5) * Math.cos(angle) - (y - 0.5) * Math.sin(angle);
let rotatedY = 0.5 + (x - 0.5) * Math.sin(angle) + (y - 0.5) * Math.cos(angle);
return [rotatedX, rotatedY];
};
for(let y = 0; y < bitmask.height; y++) { for(let y = 0; y < bitmask.height; y++) {
for(let x = 0; x < bitmask.width; x++) { for(let x = 0; x < bitmask.width; x++) {
if(bitmask.width > 16 && bitmask.height > 16) { if(bitmask.width > 16 && bitmask.height > 16) {
@ -269,22 +327,48 @@ function calculateTileStyleContour(bitmask, margin, style) {
let size = 0.9; // relative to grid size let size = 0.9; // relative to grid size
let maxAngle = Math.PI * 0.03; let maxAngle = Math.PI * 0.03;
let angle = (prng.next() * 2 - 1) * maxAngle; let angle = (prng.next() * 2 - 1) * maxAngle;
// |------ middle of the pixel ------| |-north displacement-| |-west displacement-| newPathSpec.push('M' + (x + 1) + ' ' + (y + 1));
let topLeftX = x + margin + 0.5 + ((1 - size) / 2) - 0.5 * Math.cos(angle) + 0.5 * Math.sin(angle); let tileCorners = [
let topLeftY = y + margin + 0.5 + ((1 - size) / 2) + 0.5 * Math.cos(angle) - 0.5 * Math.sin(angle) - 1; rotatePoint(0.5 - (size / 2), 0.5 - (size / 2), angle),
newPathSpec.push('M' + topLeftX.toPrecision(3) + ' ' + topLeftY.toPrecision(3)); rotatePoint(0.5 + (size / 2), 0.5 - (size / 2), angle),
newPathSpec.push('l' + (size * Math.cos(angle)).toPrecision(3) + ' ' + (size * Math.sin(angle)).toPrecision(3)); rotatePoint(0.5 + (size / 2), 0.5 + (size / 2), angle),
newPathSpec.push(('l-' + (size * Math.sin(angle)).toPrecision(3) + ' ' + (size * Math.cos(angle)).toPrecision(3)).replaceAll('--', '')); rotatePoint(0.5 - (size / 2), 0.5 + (size / 2), angle),
newPathSpec.push(('l-' + (size * Math.cos(angle)).toPrecision(3) + ' -' + (size * Math.sin(angle)).toPrecision(3)).replaceAll('--', '')); ];
newPathSpec.push(('l' + (size * Math.sin(angle)).toPrecision(3) + ' -' + (size * Math.cos(angle)).toPrecision(3)).replaceAll('--', '')); newPathSpec.push('m' + tileCorners[0][0].toPrecision(3) + ' ' + tileCorners[0][1].toPrecision(3));
newPathSpec.push('l' + (tileCorners[1][0] - tileCorners[0][0]).toPrecision(3) + ' ' + (tileCorners[1][1] - tileCorners[0][1]).toPrecision(3));
newPathSpec.push('l' + (tileCorners[2][0] - tileCorners[1][0]).toPrecision(3) + ' ' + (tileCorners[2][1] - tileCorners[1][1]).toPrecision(3));
newPathSpec.push('l' + (tileCorners[3][0] - tileCorners[2][0]).toPrecision(3) + ' ' + (tileCorners[3][1] - tileCorners[2][1]).toPrecision(3));
newPathSpec.push('z'); newPathSpec.push('z');
} else if(style == 'confetti') { } else if(style == 'confetti') {
// TODO newPathSpec.push('M' + (x + margin) + ' ' + (y + margin));
newPathSpec.push('M' + (x + margin + 0.5) + ' ' + (y + margin)); let currentShape = confettiShapes[Math.floor(prng.next() * confettiShapes.length)];
newPathSpec.push('a0.5 0.5 0 0 1 0.5 0.5'); let previousPoint = [0, 0];
newPathSpec.push('a0.5 0.5 0 0 1 -0.5 0.5'); let angle = prng.next() * Math.PI * 2;
newPathSpec.push('a0.5 0.5 0 0 1 -0.5 -0.5'); for(let segment of currentShape) {
newPathSpec.push('a0.5 0.5 0 0 1 0.5 -0.5'); segment = segment.split(' ');
let formatRotatedPoint = (i, j) => {
let rotated = rotatePoint(segment[i], segment[j], angle);
return (rotated[0] - previousPoint[0]).toPrecision(3) + ' ' + (rotated[1] - previousPoint[1]).toPrecision(3);
}
if(segment[0] == 'M') {
let rotated = rotatePoint(segment[1], segment[2], angle);
newPathSpec.push('m' + formatRotatedPoint(1, 2));
previousPoint = rotated;
} else if(segment[0] == 'L') {
let rotated = rotatePoint(segment[1], segment[2], angle);
newPathSpec.push('l' + formatRotatedPoint(1, 2));
previousPoint = rotated;
} else if(segment[0] == 'Q') {
newPathSpec.push('q' + formatRotatedPoint(1, 2) + ' ' + formatRotatedPoint(3, 4));
previousPoint = rotatePoint(segment[3], segment[4], angle)
} else if(segment[0] == 'C') {
newPathSpec.push('c' + formatRotatedPoint(1, 2) + ' ' + formatRotatedPoint(3, 4) + ' ' + formatRotatedPoint(5, 6));
previousPoint = rotatePoint(segment[5], segment[6], angle);
} else if(segment[0] == 'A') {
newPathSpec.push('a' + segment.slice(1, 6).join(' ') + ' ' + formatRotatedPoint(6, 7));
previousPoint = rotatePoint(segment[6], segment[7], angle);
}
}
newPathSpec.push('z'); newPathSpec.push('z');
} }
if(!bitmask.get(x - 1, y) && !bitmask.get(x + 1, y) && !bitmask.get(x, y - 1) && !bitmask.get(x, y + 1)) { if(!bitmask.get(x - 1, y) && !bitmask.get(x + 1, y) && !bitmask.get(x, y - 1) && !bitmask.get(x, y + 1)) {
@ -466,7 +550,7 @@ function calculateContour(bitmask, margin = 1, style = 'basic') {
contour.pdpInner.push(...Array(3).fill('v-1')); contour.pdpInner.push(...Array(3).fill('v-1'));
contour.pdpInner.push('z'); contour.pdpInner.push('z');
} }
if(style == 'dots' || style == 'rounded') { if(['dots', 'rounded', 'confetti'].includes(style)) {
contour.pdpInner = makePathSpecRound(contour.pdpInner); contour.pdpInner = makePathSpecRound(contour.pdpInner);
contour.pdpOuter = makePathSpecRound(contour.pdpOuter); contour.pdpOuter = makePathSpecRound(contour.pdpOuter);
} }