Shape variable fixes

This commit is contained in:
Sam
2025-12-31 13:47:16 +13:00
parent 8ef9705499
commit 95f35a1fa0
8 changed files with 238 additions and 80 deletions

View File

@@ -51,9 +51,10 @@
<!-- Shape definitions --> <!-- Shape definitions -->
<script src="./js/shapes/PolyTwistColourWidth.js" type="text/javascript"></script> <script src="./js/shapes/PolyTwistColourWidth.js" type="text/javascript"></script>
<script src="./js/shapes/FloralPhyllo.js" type="text/javascript"></script> <script src="./js/shapes/FloralPhyllo.js" type="text/javascript"></script>
<script src="./js/shapes/Spiral1.js" type="text/javascript"></script> <script src="./js/shapes/SpiralWormhole.js" type="text/javascript"></script>
<script src="./js/shapes/FloralAccident.js" type="text/javascript"></script> <script src="./js/shapes/FloralAccident.js" type="text/javascript"></script>
<script src="./js/shapes/FloralPhyllo_Accident.js" type="text/javascript"></script> <script src="./js/shapes/FloralPhyllo_Accident.js" type="text/javascript"></script>
<script src="./js/shapes/Nodal.js" type="text/javascript"></script>
<script src="./js/shapes/Nodal_expanding.js" type="text/javascript"></script> <script src="./js/shapes/Nodal_expanding.js" type="text/javascript"></script>
<script src="./js/shapes/Phyllotaxis.js" type="text/javascript"></script> <script src="./js/shapes/Phyllotaxis.js" type="text/javascript"></script>
<script src="./js/shapes/PhylloCone.js" type="text/javascript"></script> <script src="./js/shapes/PhylloCone.js" type="text/javascript"></script>

View File

@@ -7,52 +7,71 @@
const BUILT_IN_PRESETS = [ const BUILT_IN_PRESETS = [
{ {
name: "Poly Twist Rainbow", name: "Minds Eye is Full Of Flowers",
description: "hexagon bby", description: "Look at me",
builtIn: true, builtIn: true,
data: { data: {
version: 1, version: 1,
layers: [ layers: [
{ {
name: "PolyTwistColourWidth", name: "FloralPhyllo",
visible: true,
paused: false,
collapsed: true,
values: {
sides: 6,
width: 500,
line_width: 2,
depth: 80,
rotation: 0,
colour1: "#ff0066",
colour2: "#00ffff"
},
filters: {}
}
]
}
},
{
name: "Expanding Nodes",
description: "Pulsing nodal pattern",
builtIn: true,
data: {
version: 1,
layers: [
{
name: "Nodal_expanding",
visible: true, visible: true,
paused: false, paused: false,
collapsed: false, collapsed: false,
values: { values: {
expand: 2, width_start: 30,
points: 550, width_scale: 333,
start: 0, depth: 200,
line_width: 4, start: 625,
colour1: "#ffffff", colour1: [
colour2: "#4a9eff", 66,
colour_change: 3, 135,
speedMultiplier: 535 245
],
colour2: [
252,
3,
98
],
speedMultiplier: 500
},
filters: {},
controlBounds: {}
},
{
name: "EyePrototype",
visible: true,
paused: false,
collapsed: false,
values: {
x: 0,
y: 0,
rotate: 0,
flip: 0,
width: 531,
blink_speed: 27,
draw_spiral: 0,
spiral_full: 1,
draw_pupil: 0,
draw_expand: 0,
draw_hypno: 1,
line_width: 1,
colourPupil: [
0,
255,
251
],
colourSpiral: [
255,
0,
0
],
colourExpand: [
0,
255,
251
],
speedMultiplier: 100
}, },
filters: {}, filters: {},
controlBounds: {} controlBounds: {}

View File

@@ -3,25 +3,30 @@
*/ */
class EyePrototype extends BaseShape { class EyePrototype extends BaseShape {
static config = [ static config = [
{ type: 'header', text: '---Position---' },
{ type: 'range', min: -400, max: 400, defaultValue: 0, property: 'x' }, { type: 'range', min: -400, max: 400, defaultValue: 0, property: 'x' },
{ type: 'range', min: -400, max: 400, defaultValue: 0, property: 'y' }, { type: 'range', min: -400, max: 400, defaultValue: 0, property: 'y' },
{ type: 'range', min: -180, max: 180, defaultValue: 0, property: 'rotate' }, { type: 'range', min: -180, max: 180, defaultValue: 0, property: 'rotate' },
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'flip' }, { type: 'checkbox', defaultValue: false, property: 'flip' },
{ type: 'header', text: '--General---' },
{ type: 'range', min: 1, max: 800, defaultValue: 400, property: 'width' }, { type: 'range', min: 1, max: 800, defaultValue: 400, property: 'width' },
{ type: 'range', min: 1, max: 100, defaultValue: 5, property: 'blink_speed' }, { type: 'range', min: 1, max: 100, defaultValue: 30, property: 'blink_speed' },
{ type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_spiral' }, { type: 'range', min: 1, max: 10, defaultValue: 5, property: 'line_width' },
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'spiral_full' }, { type: 'color', defaultValue: [66, 135, 245], property: 'outline_colour' },
{ type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_pupil' }, { type: 'checkbox', defaultValue: true, property: 'draw_eyelid' },
{ type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_expand' }, { type: 'header', text: '--Effects---' },
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'draw_hypno' }, { type: 'checkbox', defaultValue: false, property: 'draw_spiral' },
{ type: 'range', min: 1, max: 10, defaultValue: 1, property: 'line_width' }, { type: 'checkbox', defaultValue: true, property: 'spiral_full' },
{ type: 'checkbox', defaultValue: false, property: 'draw_pupil' },
{ type: 'checkbox', defaultValue: false, property: 'draw_expand' },
{ type: 'checkbox', defaultValue: false, property: 'draw_hypno' },
{ type: 'checkbox', defaultValue: true, property: 'draw_wormhole' },
{ type: 'color', defaultValue: [0, 255, 251], property: 'colourPupil' }, { type: 'color', defaultValue: [0, 255, 251], property: 'colourPupil' },
{ type: 'color', defaultValue: [255, 0, 0], property: 'colourSpiral' }, { type: 'color', defaultValue: [255, 0, 0], property: 'colourSpiral' },
{ type: 'color', defaultValue: [0, 255, 251], property: 'colourExpand' }, { type: 'color', defaultValue: [0, 255, 251], property: 'colourExpand' },
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'draw_eyelid' },
]; ];
constructor(x, y, rotate, flip, width, blink_speed, draw_spiral, spiral_full, draw_pupil, draw_expand, draw_hypno, line_width, colourPupil, colourSpiral, colourExpand) { constructor(x, y, rotate, flip, width, blink_speed, line_width, outline_colour, draw_eyelid, draw_spiral, spiral_full, draw_pupil, draw_expand, draw_hypno, draw_wormhole, colourPupil, colourSpiral, colourExpand) {
super(); super();
this.x = x; this.x = x;
this.y = y; this.y = y;
@@ -30,32 +35,37 @@ class EyePrototype extends BaseShape {
this.width = width; this.width = width;
this.blink_speed = blink_speed; this.blink_speed = blink_speed;
this.line_width = line_width; this.line_width = line_width;
this.step = 0; this.outline_colour = outline_colour;
this.opening = true; this.draw_eyelid = draw_eyelid;
this.counter = 0;
this.cooldown = 0;
this.draw_spiral = draw_spiral; this.draw_spiral = draw_spiral;
this.spiral_full = spiral_full; this.spiral_full = spiral_full;
this.draw_pupil = draw_pupil; this.draw_pupil = draw_pupil;
this.draw_expand = draw_expand; this.draw_expand = draw_expand;
this.draw_hypno = draw_hypno; this.draw_hypno = draw_hypno;
this.draw_wormhole = draw_wormhole;
this.colourPupil = colourPupil; this.colourPupil = colourPupil;
this.colourSpiral = colourSpiral; this.colourSpiral = colourSpiral;
this.colourExpand = colourExpand; this.colourExpand = colourExpand;
this.step = 0;
this.opening = true;
this.counter = 0;
this.cooldown = 0;
this.centerPulse = new CircleExpand(10, 30, 1, 0, [45, 129, 252], [252, 3, 98]); this.centerPulse = new CircleExpand(10, 30, 1, 0, [45, 129, 252], [252, 3, 98]);
this.wormhole = new SpiralWormhole(20, 120, [66, 135, 245]);
} }
drawEyelid(rotation) { drawEyelid(rotation, lineWidth) {
ctx.strokeStyle = "orange"; ctx.strokeStyle = "black";
const relCenterX = centerX + this.x; const relCenterX = centerX + this.x;
const relCenterY = centerY + this.y; const relCenterY = centerY + this.y;
rotation *= (this.speedMultiplier / 100); rotation *= (this.speedMultiplier / 100);
ctx.lineWidth = 1; ctx.lineWidth = lineWidth;
ctx.strokeStyle = colourToText(this.outline_colour);
ctx.beginPath(); ctx.beginPath();
let newPoint = 0; let newPoint = 0;
let newPoint1 = 0; let newPoint1 = 0;
const addedRotate = this.flip ? 90 : 0; const addedRotate = this.flip ? 90 : 0;
newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate); newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate);
ctx.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]); ctx.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]);
newPoint = rotatePoint(0, -rotation / 400 * this.width, this.rotate + addedRotate); newPoint = rotatePoint(0, -rotation / 400 * this.width, this.rotate + addedRotate);
@@ -76,7 +86,7 @@ class EyePrototype extends BaseShape {
let newPoint = 0; let newPoint = 0;
let newPoint1 = 0; let newPoint1 = 0;
const addedRotate = this.flip ? 90 : 0; const addedRotate = this.flip ? 90 : 0;
const squarePath = new Path2D(); const squarePath = new Path2D();
newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate); newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate);
squarePath.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]); squarePath.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]);
@@ -116,7 +126,7 @@ class EyePrototype extends BaseShape {
ctx.moveTo(centerX, centerY); ctx.moveTo(centerX, centerY);
ctx.beginPath(); ctx.beginPath();
const max = this.spiral_full ? this.width : this.width / 2; const max = this.spiral_full ? this.width : this.width / 2;
for (let i = 0; i < max; i++) { for (let i = 0; i < max; i++) {
const angle = 0.1 * i; const angle = 0.1 * i;
const x = centerX + (a + b * angle) * Math.cos(angle + step / 2); const x = centerX + (a + b * angle) * Math.cos(angle + step / 2);
@@ -160,28 +170,31 @@ class EyePrototype extends BaseShape {
ctx.fillStyle = "black"; ctx.fillStyle = "black";
ctx.save(); ctx.save();
this.drawEyelid(outputRotation); this.drawEyelid(outputRotation, this.line_width);
this.eyelidCut(outputRotation); this.eyelidCut(outputRotation);
if (Math.floor(this.counter % (this.width / 4)) === 0) { if (Math.floor(this.counter % (this.width / 4)) === 0) {
this.counter = 0; this.counter = 0;
} }
ctx.fillStyle = "black"; ctx.fillStyle = "black";
ctx.fillRect(this.x - this.width / 2 + centerX, 0, this.width, ctx.canvas.height); ctx.fillRect(this.x - this.width / 2 + centerX, 0, this.width, ctx.canvas.height);
if (this.draw_expand) {
this.drawGrowEye(this.width / 4 + this.counter);
}
if (this.draw_hypno) { if (this.draw_hypno) {
this.centerPulse.draw(elapsed); this.centerPulse.draw(elapsed);
} }
if (this.draw_wormhole) {
this.wormhole.draw(elapsed);
}
if (this.draw_expand) {
this.drawGrowEye(this.width / 4 + this.counter);
}
if (this.draw_spiral) { if (this.draw_spiral) {
this.drawSpiral(elapsed); this.drawSpiral(elapsed);
} }
if (this.draw_pupil) { if (this.draw_pupil) {
this.drawCircle(this.width / 4); this.drawCircle(this.width / 4);
} }

View File

@@ -3,16 +3,18 @@
*/ */
class FloralPhyllo extends BaseShape { class FloralPhyllo extends BaseShape {
static config = [ static config = [
{ type: 'range', min: 1, max: 600, defaultValue: 300, property: 'width' }, { type: 'range', min: 1, max: 100, defaultValue: 60, property: 'width_start' },
{ type: 'range', min: 1, max: 300, defaultValue: 150, property: 'depth' }, { type: 'range', min: 1, max: 1000, defaultValue: 650, property: 'width_scale' },
{ type: 'range', min: 1, max: 1000, defaultValue: 200, property: 'depth' },
{ type: 'range', min: 0, max: 3141, defaultValue: 0, property: 'start' }, { type: 'range', min: 0, max: 3141, defaultValue: 0, property: 'start' },
{ type: 'color', defaultValue: [66, 135, 245], property: 'colour1' }, { type: 'color', defaultValue: [66, 135, 245], property: 'colour1' },
{ type: 'color', defaultValue: [252, 3, 98], property: 'colour2' }, { type: 'color', defaultValue: [252, 3, 98], property: 'colour2' },
]; ];
constructor(width, depth, start, colour1, colour2) { constructor(width_start,width_scale, depth, start, colour1, colour2) {
super(); super();
this.width = width; this.width_start = width_start;
this.width_scale = width_scale;
this.depth = depth; this.depth = depth;
this.start = start; this.start = start;
this.colour1 = colour1; this.colour1 = colour1;
@@ -32,7 +34,7 @@ class FloralPhyllo extends BaseShape {
const x = r * Math.cos(a) + centerX; const x = r * Math.cos(a) + centerX;
const y = r * Math.sin(a) + centerY; const y = r * Math.sin(a) + centerY;
drawEyelid(n * 2.4 + 40, x, y, ncolour); drawPetal(n * this.width_scale/100 + this.width_start, x, y, ncolour);
} }
} }
} }

56
docs/js/shapes/Nodal.js Normal file
View File

@@ -0,0 +1,56 @@
class Nodal extends BaseShape {
static config = [
{ type: 'range', min: 0, max: 360, defaultValue: 0, property: 'shift' },
{ type: 'range', min: 10, max: 800, defaultValue: 400, property: 'radius' },
{ type: 'range', min: 3, max: 500, defaultValue: 100, property: 'points' },
{ type: 'range', min: 1, max: 250, defaultValue: 41, property: 'step' },
{ type: 'range', min: 1, max: 20, defaultValue: 2, property: 'lineWidth' },
{ type: 'color', defaultValue: [64, 199, 119], property: 'colour' },
];
constructor(shift, radius, points, step, lineWidth, colour) {
super();
this.shift = shift;
this.radius = radius;
this.points = points;
this.step = step;
this.lineWidth = lineWidth;
this.colour = colour;
}
draw(elapsed) {
const rotation = elapsed * (this.speedMultiplier / 100) + this.shift;
const angle = 360 / this.points * this.step;
ctx.beginPath();
ctx.moveTo(
centerX + Math.cos(this.rad(angle + rotation)) * this.radius,
centerY + Math.sin(this.rad(angle + rotation)) * this.radius
);
let totalMoves = 1;
while ((totalMoves * this.step) % this.points !== 0) {
totalMoves++;
}
totalMoves++;
for (let z = 1; z <= totalMoves; z++) {
ctx.lineTo(
centerX + Math.cos(this.rad(angle * z + rotation)) * this.radius,
centerY + Math.sin(this.rad(angle * z + rotation)) * this.radius
);
}
ctx.strokeStyle = colourToText(this.colour);
ctx.lineWidth = this.lineWidth;
ctx.stroke();
}
rad(degrees) {
return degrees * (Math.PI / 180);
}
}
console.log('Nodal shape loaded');
shapeRegistry.register('Nodal', Nodal);

View File

@@ -3,16 +3,15 @@
*/ */
class Nodal_expanding extends BaseShape { class Nodal_expanding extends BaseShape {
static config = [ static config = [
{ type: 'range', min: 0, max: 100, defaultValue: 0.1, property: 'expand' }, { type: 'range', min: 0, max: 100, defaultValue: 1, property: 'expand' },
{ type: 'range', min: 1, max: 1000, defaultValue: 150, property: 'points' }, { type: 'range', min: 1, max: 10000, defaultValue: 1000, property: 'points' },
{ type: 'range', min: 1, max: 360, defaultValue: 0, property: 'start' }, { type: 'range', min: 1, max: 360, defaultValue: 32, property: 'start' },
{ type: 'range', min: 1, max: 10, defaultValue: 6, property: 'line_width' }, { type: 'range', min: 1, max: 10, defaultValue: 6, property: 'line_width' },
{ type: 'color', defaultValue: [45, 129, 252], property: 'colour1' }, { type: 'color', defaultValue: [45, 129, 252], property: 'colour1' },
{ type: 'color', defaultValue: [252, 3, 98], property: 'colour2' }, { type: 'color', defaultValue: [252, 3, 98], property: 'colour2' },
{ type: 'range', min: 0, max: 10, defaultValue: 5, property: 'colour_change' },
]; ];
constructor(expand, points, start, line_width, colour1, colour2, colour_change) { constructor(expand, points, start, line_width, colour1, colour2) {
super(); super();
this.expand = expand; this.expand = expand;
this.points = points; this.points = points;
@@ -20,7 +19,6 @@ class Nodal_expanding extends BaseShape {
this.line_width = line_width; this.line_width = line_width;
this.colour1 = colour1; this.colour1 = colour1;
this.colour2 = colour2; this.colour2 = colour2;
this.colour_change = colour_change;
} }
draw(elapsed) { draw(elapsed) {

View File

@@ -1,7 +1,7 @@
/** /**
* Spiral1 - Dual-direction spiral pattern * Spiral1 - Dual-direction spiral pattern
*/ */
class Spiral1 extends BaseShape { class SpiralWormhole extends BaseShape {
static config = [ static config = [
{ type: 'range', min: 1, max: 50, defaultValue: 20, property: 'sides' }, { type: 'range', min: 1, max: 50, defaultValue: 20, property: 'sides' },
{ type: 'range', min: 1, max: 600, defaultValue: 240, property: 'width' }, { type: 'range', min: 1, max: 600, defaultValue: 240, property: 'width' },
@@ -16,6 +16,7 @@ class Spiral1 extends BaseShape {
} }
draw(elapsed) { draw(elapsed) {
elapsed *= 10
this.updateFilters(elapsed); this.updateFilters(elapsed);
const rotation = elapsed * (this.speedMultiplier / 100); const rotation = elapsed * (this.speedMultiplier / 100);
const rot = Math.round((this.sides - 2) * 180 / this.sides * 2); const rot = Math.round((this.sides - 2) * 180 / this.sides * 2);
@@ -57,4 +58,4 @@ class Spiral1 extends BaseShape {
} }
} }
shapeRegistry.register('Spiral1', Spiral1); shapeRegistry.register('SpiralWormhole', SpiralWormhole);

View File

@@ -53,6 +53,74 @@ function rgbArrayToHex(rgb) {
return "#" + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1); return "#" + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1);
} }
/**
* Cached petal path for efficient reuse (created once, transformed many times)
* @type {Path2D|null}
*/
let _petalPathCache = null;
/**
* Get or create the cached petal path (unit size, pointing right from origin)
* @returns {Path2D}
*/
function getPetalPath() {
if (!_petalPathCache) {
_petalPathCache = new Path2D();
_petalPathCache.moveTo(0, 0);
_petalPathCache.quadraticCurveTo(0.5, 0.5, 1, 0);
_petalPathCache.quadraticCurveTo(0.5, -0.5, 0, 0);
}
return _petalPathCache;
}
/**
* Draw a petal shape efficiently using canvas transforms
* Much faster than drawEyelid - uses cached path and transforms
* @param {number} width - Width/length of the petal
* @param {number} x - X position
* @param {number} y - Y position
* @param {string} colour - Fill color (CSS string)
*/
function drawPetal(width, x, y, colour, line_width = 2) {
const angle = Math.atan2(y - centerY, x - centerX);
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.scale(width, width);
ctx.fillStyle = colour;
ctx.fill(getPetalPath());
ctx.lineWidth = 2 / width; // Scale line width inversely so it stays consistent
ctx.strokeStyle = "black";
ctx.stroke(getPetalPath());
ctx.restore();
}
/**
* Draw multiple petals efficiently by batching transforms
* Even faster for large numbers - minimizes state changes
* @param {Array<{width: number, x: number, y: number, colour: string}>} petals
*/
function drawPetalsBatch(petals) {
const path = getPetalPath();
for (let i = 0; i < petals.length; i++) {
const p = petals[i];
const angle = Math.atan2(p.y - centerY, p.x - centerX);
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate(angle);
ctx.scale(p.width, p.width);
ctx.fillStyle = p.colour;
ctx.fill(path);
ctx.restore();
}
}
/** /**
* Linear interpolation between two hex colors * Linear interpolation between two hex colors
* @param {string} a - Start hex color * @param {string} a - Start hex color