Compare commits

..

1 Commits

Author SHA1 Message Date
3c06f5e587 feat: pattern editor 2025-10-12 13:37:08 +02:00
4 changed files with 72 additions and 58 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

After

Width:  |  Height:  |  Size: 244 KiB

View File

@@ -103,8 +103,16 @@
</div> </div>
</div> </div>
<!-- Add Layer Button --> <!-- Add Layer Section -->
<div class="editor-section-compact"> <div class="editor-section-compact">
<h3 class="section-title-compact">Add Layer</h3>
<div class="editor-input-wrapper">
<label>Layer Type</label>
<select id="editor-layer-type">
<option value="shape">Shape</option>
<option value="pattern">Pattern</option>
</select>
</div>
<button class="btn btn-primary btn-full-width" id="editor-add-layer"> Add Layer</button> <button class="btn btn-primary btn-full-width" id="editor-add-layer"> Add Layer</button>
</div> </div>

View File

@@ -138,6 +138,38 @@ class CanvasRenderer {
}; };
} }
case 'palette': {
const stops = colorConfig.stops || [
{ position: 0, color: '000000' },
{ position: 1, color: 'ffffff' }
];
return (t) => {
const clamped = this.clamp(t, 0, 1);
// Find the two stops to interpolate between
let stop1 = stops[0];
let stop2 = stops[stops.length - 1];
for (let i = 0; i < stops.length - 1; i++) {
if (clamped >= stops[i].position && clamped <= stops[i + 1].position) {
stop1 = stops[i];
stop2 = stops[i + 1];
break;
}
}
// Interpolate between the two stops
const range = stop2.position - stop1.position;
const localT = range > 0 ? (clamped - stop1.position) / range : 0;
const rgb1 = this.hexToRgb(stop1.color);
const rgb2 = this.hexToRgb(stop2.color);
return this.rgbToHex(this.lerpRgb(rgb1, rgb2, localT));
};
}
default: default:
return () => 'ff0000'; return () => 'ff0000';
} }

View File

@@ -157,15 +157,14 @@ class PresetEditor {
} }
addLayer() { addLayer() {
// Create a default shape layer const layerType = document.getElementById('editor-layer-type')?.value || 'shape';
const layer = {
type: 'shape', let layer;
shape: 'circle', if (layerType === 'pattern') {
position: { x: 8, y: 8 }, layer = this.createPatternLayer();
size: { radius: 3, width: 5, height: 5 }, } else {
color: { type: 'solid', value: 'ff0000' }, layer = this.createShapeLayer();
intensity: 1.0 }
};
this.configuration.layers.push(layer); this.configuration.layers.push(layer);
@@ -198,64 +197,39 @@ class PresetEditor {
} }
createShapeLayer() { createShapeLayer() {
const shape = document.getElementById('editor-shape-type')?.value || 'circle'; return {
const colorType = document.getElementById('editor-color-type')?.value || 'solid';
const colorInput = document.getElementById('editor-color-value');
// Get color value and convert from #RRGGBB to RRGGBB format
let colorValue = 'ff0000';
if (colorInput && colorInput.value) {
colorValue = colorInput.value.replace('#', '');
}
const animationType = document.getElementById('editor-animation-type')?.value;
const layer = {
type: 'shape', type: 'shape',
shape: shape, shape: 'circle',
position: { position: { x: 8, y: 8 },
x: parseFloat(document.getElementById('editor-pos-x')?.value || 8), size: { radius: 3, width: 5, height: 5 },
y: parseFloat(document.getElementById('editor-pos-y')?.value || 8) color: { type: 'solid', value: 'ff0000' },
}, intensity: 1.0
size: {
radius: parseFloat(document.getElementById('editor-size-radius')?.value || 3),
width: parseFloat(document.getElementById('editor-size-width')?.value || 5),
height: parseFloat(document.getElementById('editor-size-height')?.value || 5)
},
color: {
type: colorType,
value: colorValue
},
intensity: parseFloat(document.getElementById('editor-intensity')?.value || 1.0),
}; };
if (animationType && animationType !== 'none') {
layer.animation = this.createAnimation(animationType);
}
return layer;
} }
createPatternLayer() { createPatternLayer() {
const pattern = document.getElementById('editor-pattern-type')?.value || 'radial';
const colorType = document.getElementById('editor-color-type')?.value || 'solid';
const colorInput = document.getElementById('editor-color-value');
// Get color value and convert from #RRGGBB to RRGGBB format
let colorValue = 'ff0000';
if (colorInput && colorInput.value) {
colorValue = colorInput.value.replace('#', '');
}
return { return {
type: 'pattern', type: 'pattern',
pattern: pattern, pattern: 'spiral',
color: { color: {
type: colorType, type: 'palette',
value: colorValue stops: [
{ position: 0.0, color: 'ff0000' },
{ position: 0.17, color: 'ff8800' },
{ position: 0.33, color: 'ffff00' },
{ position: 0.5, color: '00ff00' },
{ position: 0.67, color: '0088ff' },
{ position: 0.83, color: '8800ff' },
{ position: 1.0, color: 'ff0088' }
]
}, },
intensity: 1.0, intensity: 1.0,
params: this.getPatternParams(pattern) params: {
centerX: 8,
centerY: 8,
arms: 5,
rotationSpeed: 1.0
}
}; };
} }