diff --git a/docs/index.html b/docs/index.html
index 64a9f75..e9fe447 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -51,9 +51,10 @@
-
+
+
diff --git a/docs/js/presets.js b/docs/js/presets.js
index b4e552e..204d83b 100644
--- a/docs/js/presets.js
+++ b/docs/js/presets.js
@@ -7,52 +7,71 @@
const BUILT_IN_PRESETS = [
{
- name: "Poly Twist Rainbow",
- description: "hexagon bby",
+ name: "Minds Eye is Full Of Flowers",
+ description: "Look at me",
builtIn: true,
data: {
version: 1,
layers: [
{
- name: "PolyTwistColourWidth",
- 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",
+ name: "FloralPhyllo",
visible: true,
paused: false,
collapsed: false,
values: {
- expand: 2,
- points: 550,
- start: 0,
- line_width: 4,
- colour1: "#ffffff",
- colour2: "#4a9eff",
- colour_change: 3,
- speedMultiplier: 535
+ width_start: 30,
+ width_scale: 333,
+ depth: 200,
+ start: 625,
+ colour1: [
+ 66,
+ 135,
+ 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: {},
controlBounds: {}
diff --git a/docs/js/shapes/EyePrototype.js b/docs/js/shapes/EyePrototype.js
index 97d2b2b..c08008f 100644
--- a/docs/js/shapes/EyePrototype.js
+++ b/docs/js/shapes/EyePrototype.js
@@ -3,25 +3,30 @@
*/
class EyePrototype extends BaseShape {
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: 'y' },
{ 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: 100, defaultValue: 5, property: 'blink_speed' },
- { type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_spiral' },
- { type: 'range', min: 0, max: 1, defaultValue: 1, property: 'spiral_full' },
- { type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_pupil' },
- { type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_expand' },
- { type: 'range', min: 0, max: 1, defaultValue: 1, property: 'draw_hypno' },
- { type: 'range', min: 1, max: 10, defaultValue: 1, property: 'line_width' },
+ { type: 'range', min: 1, max: 100, defaultValue: 30, property: 'blink_speed' },
+ { type: 'range', min: 1, max: 10, defaultValue: 5, property: 'line_width' },
+ { type: 'color', defaultValue: [66, 135, 245], property: 'outline_colour' },
+ { type: 'checkbox', defaultValue: true, property: 'draw_eyelid' },
+ { type: 'header', text: '--Effects---' },
+ { type: 'checkbox', defaultValue: false, property: 'draw_spiral' },
+ { 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: [255, 0, 0], property: 'colourSpiral' },
{ 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();
this.x = x;
this.y = y;
@@ -30,32 +35,37 @@ class EyePrototype extends BaseShape {
this.width = width;
this.blink_speed = blink_speed;
this.line_width = line_width;
- this.step = 0;
- this.opening = true;
- this.counter = 0;
- this.cooldown = 0;
+ this.outline_colour = outline_colour;
+ this.draw_eyelid = draw_eyelid;
this.draw_spiral = draw_spiral;
this.spiral_full = spiral_full;
this.draw_pupil = draw_pupil;
this.draw_expand = draw_expand;
this.draw_hypno = draw_hypno;
+ this.draw_wormhole = draw_wormhole;
this.colourPupil = colourPupil;
this.colourSpiral = colourSpiral;
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.wormhole = new SpiralWormhole(20, 120, [66, 135, 245]);
}
- drawEyelid(rotation) {
- ctx.strokeStyle = "orange";
+ drawEyelid(rotation, lineWidth) {
+ ctx.strokeStyle = "black";
const relCenterX = centerX + this.x;
const relCenterY = centerY + this.y;
rotation *= (this.speedMultiplier / 100);
- ctx.lineWidth = 1;
+ ctx.lineWidth = lineWidth;
+ ctx.strokeStyle = colourToText(this.outline_colour);
ctx.beginPath();
let newPoint = 0;
let newPoint1 = 0;
const addedRotate = this.flip ? 90 : 0;
-
+
newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate);
ctx.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]);
newPoint = rotatePoint(0, -rotation / 400 * this.width, this.rotate + addedRotate);
@@ -76,7 +86,7 @@ class EyePrototype extends BaseShape {
let newPoint = 0;
let newPoint1 = 0;
const addedRotate = this.flip ? 90 : 0;
-
+
const squarePath = new Path2D();
newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate);
squarePath.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]);
@@ -116,7 +126,7 @@ class EyePrototype extends BaseShape {
ctx.moveTo(centerX, centerY);
ctx.beginPath();
const max = this.spiral_full ? this.width : this.width / 2;
-
+
for (let i = 0; i < max; i++) {
const angle = 0.1 * i;
const x = centerX + (a + b * angle) * Math.cos(angle + step / 2);
@@ -160,28 +170,31 @@ class EyePrototype extends BaseShape {
ctx.fillStyle = "black";
ctx.save();
- this.drawEyelid(outputRotation);
+ this.drawEyelid(outputRotation, this.line_width);
this.eyelidCut(outputRotation);
-
+
if (Math.floor(this.counter % (this.width / 4)) === 0) {
this.counter = 0;
}
-
+
ctx.fillStyle = "black";
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) {
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) {
this.drawSpiral(elapsed);
}
-
+
if (this.draw_pupil) {
this.drawCircle(this.width / 4);
}
diff --git a/docs/js/shapes/FloralPhyllo.js b/docs/js/shapes/FloralPhyllo.js
index 90b39b8..793e5a1 100644
--- a/docs/js/shapes/FloralPhyllo.js
+++ b/docs/js/shapes/FloralPhyllo.js
@@ -3,16 +3,18 @@
*/
class FloralPhyllo extends BaseShape {
static config = [
- { type: 'range', min: 1, max: 600, defaultValue: 300, property: 'width' },
- { type: 'range', min: 1, max: 300, defaultValue: 150, property: 'depth' },
+ { type: 'range', min: 1, max: 100, defaultValue: 60, property: 'width_start' },
+ { 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: 'color', defaultValue: [66, 135, 245], property: 'colour1' },
{ type: 'color', defaultValue: [252, 3, 98], property: 'colour2' },
];
- constructor(width, depth, start, colour1, colour2) {
+ constructor(width_start,width_scale, depth, start, colour1, colour2) {
super();
- this.width = width;
+ this.width_start = width_start;
+ this.width_scale = width_scale;
this.depth = depth;
this.start = start;
this.colour1 = colour1;
@@ -32,7 +34,7 @@ class FloralPhyllo extends BaseShape {
const x = r * Math.cos(a) + centerX;
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);
}
}
}
diff --git a/docs/js/shapes/Nodal.js b/docs/js/shapes/Nodal.js
new file mode 100644
index 0000000..28db7c2
--- /dev/null
+++ b/docs/js/shapes/Nodal.js
@@ -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);
\ No newline at end of file
diff --git a/docs/js/shapes/Nodal_expanding.js b/docs/js/shapes/Nodal_expanding.js
index fc876d1..0099275 100644
--- a/docs/js/shapes/Nodal_expanding.js
+++ b/docs/js/shapes/Nodal_expanding.js
@@ -3,16 +3,15 @@
*/
class Nodal_expanding extends BaseShape {
static config = [
- { type: 'range', min: 0, max: 100, defaultValue: 0.1, property: 'expand' },
- { type: 'range', min: 1, max: 1000, defaultValue: 150, property: 'points' },
- { type: 'range', min: 1, max: 360, defaultValue: 0, property: 'start' },
+ { type: 'range', min: 0, max: 100, defaultValue: 1, property: 'expand' },
+ { type: 'range', min: 1, max: 10000, defaultValue: 1000, property: 'points' },
+ { type: 'range', min: 1, max: 360, defaultValue: 32, property: 'start' },
{ type: 'range', min: 1, max: 10, defaultValue: 6, property: 'line_width' },
{ type: 'color', defaultValue: [45, 129, 252], property: 'colour1' },
{ 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();
this.expand = expand;
this.points = points;
@@ -20,7 +19,6 @@ class Nodal_expanding extends BaseShape {
this.line_width = line_width;
this.colour1 = colour1;
this.colour2 = colour2;
- this.colour_change = colour_change;
}
draw(elapsed) {
diff --git a/docs/js/shapes/Spiral1.js b/docs/js/shapes/SpiralWormhole.js
similarity index 93%
rename from docs/js/shapes/Spiral1.js
rename to docs/js/shapes/SpiralWormhole.js
index e2e5646..98c13b1 100644
--- a/docs/js/shapes/Spiral1.js
+++ b/docs/js/shapes/SpiralWormhole.js
@@ -1,7 +1,7 @@
/**
* Spiral1 - Dual-direction spiral pattern
*/
-class Spiral1 extends BaseShape {
+class SpiralWormhole extends BaseShape {
static config = [
{ type: 'range', min: 1, max: 50, defaultValue: 20, property: 'sides' },
{ type: 'range', min: 1, max: 600, defaultValue: 240, property: 'width' },
@@ -16,6 +16,7 @@ class Spiral1 extends BaseShape {
}
draw(elapsed) {
+ elapsed *= 10
this.updateFilters(elapsed);
const rotation = elapsed * (this.speedMultiplier / 100);
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);
diff --git a/docs/js/utils/helpers.js b/docs/js/utils/helpers.js
index 2e2bdcf..c7a0ce3 100644
--- a/docs/js/utils/helpers.js
+++ b/docs/js/utils/helpers.js
@@ -53,6 +53,74 @@ function rgbArrayToHex(rgb) {
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
* @param {string} a - Start hex color