import { generateFlowerSVGString } from '../utils/flower-svg';
/**
* Seeded random number generator for deterministic randomness
*/
function seededRandom(seed: string): () => number {
let hash = 0;
for (let i = 0; i < seed.length; i++) {
hash = ((hash << 5) - hash) + seed.charCodeAt(i);
hash = hash & hash; // Convert to 32bit integer
}
let state = Math.abs(hash);
return function () {
state = (state * 1103515245 + 12345) & 0x7fffffff;
return state / 0x7fffffff;
};
}
/**
* Lighten or darken a hex color
*/
function adjustColor(hex: string, amount: number): string {
// Remove # if present
hex = hex.replace(/^#/, '');
// Parse RGB
let r = parseInt(hex.slice(0, 2), 16);
let g = parseInt(hex.slice(2, 4), 16);
let b = parseInt(hex.slice(4, 6), 16);
// Adjust
r = Math.max(0, Math.min(255, r + amount));
g = Math.max(0, Math.min(255, g + amount));
b = Math.max(0, Math.min(255, b + amount));
// Convert back to hex
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
}
/**
* Generate flower parameters from DID
*/
interface FlowerParams {
// Petal configuration
petalCount: number;
petalShape: 'round' | 'pointed' | 'wavy' | 'heart' | 'tulip';
petalSize: number;
petalRotation: number;
// Layering (Proposal 1)
layerCount: number;
layerRotationOffset: number;
layerSizeDecay: number;
// Per-petal variation (Proposal 3)
petalSizeJitter: number;
petalAngleJitter: number;
petalCurveJitter: number;
// Center style (Proposal 4)
centerStyle: 'simple' | 'stamen' | 'spiral' | 'dots' | 'ring';
centerSize: number;
stamenCount: number;
// Stem and leaves
hasStem: boolean;
hasLeaves: boolean;
leafStyle: 'ellipse' | 'pointed' | 'serrated';
// Colors
primaryColor: string;
secondaryColor: string;
centerColor: string;
stamenTipColor: string;
}
function generateFlowerParams(did: string, colors: any): FlowerParams {
const rng = seededRandom(did);
// Number of petals: 4-10 (increased range)
const petalCount = Math.floor(rng() * 7) + 4;
// Petal shape: expanded options
const shapeIndex = Math.floor(rng() * 5);
const petalShape = ['round', 'pointed', 'wavy', 'heart', 'tulip'][shapeIndex] as FlowerParams['petalShape'];
// Petal size: 0.5 to 0.9 (relative to flower size)
const petalSize = 0.5 + rng() * 0.4;
// Initial rotation offset
const petalRotation = rng() * 360;
// Layer configuration (Proposal 1)
const layerCount = Math.floor(rng() * 3) + 1; // 1-3 layers
const layerRotationOffset = 15 + rng() * 30; // 15-45 degrees offset between layers
const layerSizeDecay = 0.6 + rng() * 0.2; // Each inner layer is 60-80% of outer
// Per-petal variation (Proposal 3)
const petalSizeJitter = 0.1 + rng() * 0.15; // 10-25% size variation
const petalAngleJitter = 3 + rng() * 7; // 3-10 degrees angle jitter
const petalCurveJitter = 0.1 + rng() * 0.2; // 10-30% curve variation
// Center style (Proposal 4)
const centerStyleIndex = Math.floor(rng() * 5);
const centerStyle = ['simple', 'stamen', 'spiral', 'dots', 'ring'][centerStyleIndex] as FlowerParams['centerStyle'];
const centerSize = 0.12 + rng() * 0.12; // 12-24% of flower size
const stamenCount = Math.floor(rng() * 8) + 5; // 5-12 stamens
// 60% chance of stem
const hasStem = rng() > 0.4;
// 50% chance of leaves (only if stem exists)
const hasLeaves = hasStem && rng() > 0.5;
// Leaf style
const leafStyleIndex = Math.floor(rng() * 3);
const leafStyle = ['ellipse', 'pointed', 'serrated'][leafStyleIndex] as FlowerParams['leafStyle'];
// Color variations
const primaryColor = colors.primary || '#ff6b9d';
const secondaryColor = colors.accent || colors.primary || '#ff9ecd';
const centerColor = colors.text || colors.primary || '#4a4a4a';
const stamenTipColor = colors.accent || adjustColor(primaryColor, 60);
return {
petalCount,
petalShape,
petalSize,
petalRotation,
layerCount,
layerRotationOffset,
layerSizeDecay,
petalSizeJitter,
petalAngleJitter,
petalCurveJitter,
centerStyle,
centerSize,
stamenCount,
hasStem,
hasLeaves,
leafStyle,
primaryColor,
secondaryColor,
centerColor,
stamenTipColor
};
}
/**
* Generate SVG path for a petal based on shape with organic variation
*/
function generatePetalPath(
shape: FlowerParams['petalShape'],
baseSize: number,
centerX: number,
centerY: number,
angle: number,
sizeMultiplier: number,
curveVariation: number
): string {
const rad = (angle * Math.PI) / 180;
const size = baseSize * sizeMultiplier;
const petalLength = size * 40;
const petalWidth = size * 20 * (1 + curveVariation * 0.3);
// Calculate petal tip position
const tipX = centerX + Math.cos(rad) * petalLength;
const tipY = centerY + Math.sin(rad) * petalLength;
// Calculate control points for curves
const perpAngle = rad + Math.PI / 2;
const controlOffset = petalWidth * 0.5 * (1 + curveVariation * 0.2);
const leftControlX = centerX + Math.cos(perpAngle) * controlOffset;
const leftControlY = centerY + Math.sin(perpAngle) * controlOffset;
const rightControlX = centerX - Math.cos(perpAngle) * controlOffset;
const rightControlY = centerY - Math.sin(perpAngle) * controlOffset;
switch (shape) {
case 'round': {
// Rounded petal using quadratic curves
const leftCurveX = tipX + Math.cos(perpAngle) * petalWidth * 0.3;
const leftCurveY = tipY + Math.sin(perpAngle) * petalWidth * 0.3;
const rightCurveX = tipX - Math.cos(perpAngle) * petalWidth * 0.3;
const rightCurveY = tipY - Math.sin(perpAngle) * petalWidth * 0.3;
return `M ${centerX} ${centerY} Q ${leftControlX} ${leftControlY} ${leftCurveX} ${leftCurveY} Q ${tipX} ${tipY} ${rightCurveX} ${rightCurveY} Q ${rightControlX} ${rightControlY} ${centerX} ${centerY} Z`;
}
case 'pointed': {
// Pointed petal with elegant curves leading to sharp tip
// Control points for smooth S-curve edges
const bulgeFactor = petalWidth * 0.45;
const bulgePoint = 0.4; // Where the petal is widest
// Left edge curve points
const leftBulgeX = centerX + Math.cos(rad) * petalLength * bulgePoint + Math.cos(perpAngle) * bulgeFactor;
const leftBulgeY = centerY + Math.sin(rad) * petalLength * bulgePoint + Math.sin(perpAngle) * bulgeFactor;
const leftNarrowX = centerX + Math.cos(rad) * petalLength * 0.75 + Math.cos(perpAngle) * bulgeFactor * 0.4;
const leftNarrowY = centerY + Math.sin(rad) * petalLength * 0.75 + Math.sin(perpAngle) * bulgeFactor * 0.4;
// Right edge curve points
const rightBulgeX = centerX + Math.cos(rad) * petalLength * bulgePoint - Math.cos(perpAngle) * bulgeFactor;
const rightBulgeY = centerY + Math.sin(rad) * petalLength * bulgePoint - Math.sin(perpAngle) * bulgeFactor;
const rightNarrowX = centerX + Math.cos(rad) * petalLength * 0.75 - Math.cos(perpAngle) * bulgeFactor * 0.4;
const rightNarrowY = centerY + Math.sin(rad) * petalLength * 0.75 - Math.sin(perpAngle) * bulgeFactor * 0.4;
// Use cubic bezier for smooth organic curves
return `M ${centerX} ${centerY} C ${leftControlX} ${leftControlY} ${leftBulgeX} ${leftBulgeY} ${leftNarrowX} ${leftNarrowY} Q ${tipX} ${tipY} ${rightNarrowX} ${rightNarrowY} C ${rightBulgeX} ${rightBulgeY} ${rightControlX} ${rightControlY} ${centerX} ${centerY} Z`;
}
case 'wavy': {
// Wavy petal with curved edges
const wave1X = centerX + Math.cos(rad) * petalLength * 0.3 + Math.cos(perpAngle) * petalWidth * 0.5;
const wave1Y = centerY + Math.sin(rad) * petalLength * 0.3 + Math.sin(perpAngle) * petalWidth * 0.5;
const wave2X = centerX + Math.cos(rad) * petalLength * 0.6 + Math.cos(perpAngle) * petalWidth * 0.3;
const wave2Y = centerY + Math.sin(rad) * petalLength * 0.6 + Math.sin(perpAngle) * petalWidth * 0.3;
const wave3X = centerX + Math.cos(rad) * petalLength * 0.6 - Math.cos(perpAngle) * petalWidth * 0.3;
const wave3Y = centerY + Math.sin(rad) * petalLength * 0.6 - Math.sin(perpAngle) * petalWidth * 0.3;
const wave4X = centerX + Math.cos(rad) * petalLength * 0.3 - Math.cos(perpAngle) * petalWidth * 0.5;
const wave4Y = centerY + Math.sin(rad) * petalLength * 0.3 - Math.sin(perpAngle) * petalWidth * 0.5;
return `M ${centerX} ${centerY} Q ${leftControlX} ${leftControlY} ${wave1X} ${wave1Y} Q ${wave2X} ${wave2Y} ${tipX} ${tipY} Q ${wave3X} ${wave3Y} ${wave4X} ${wave4Y} Q ${rightControlX} ${rightControlY} ${centerX} ${centerY} Z`;
}
case 'heart': {
// Heart-shaped petal with notch at tip
const notchDepth = petalLength * 0.15;
const lobeWidth = petalWidth * 0.6;
// Left lobe
const leftLobeX = tipX + Math.cos(perpAngle) * lobeWidth - Math.cos(rad) * notchDepth * 0.5;
const leftLobeY = tipY + Math.sin(perpAngle) * lobeWidth - Math.sin(rad) * notchDepth * 0.5;
// Right lobe
const rightLobeX = tipX - Math.cos(perpAngle) * lobeWidth - Math.cos(rad) * notchDepth * 0.5;
const rightLobeY = tipY - Math.sin(perpAngle) * lobeWidth - Math.sin(rad) * notchDepth * 0.5;
// Notch point
const notchX = tipX - Math.cos(rad) * notchDepth;
const notchY = tipY - Math.sin(rad) * notchDepth;
return `M ${centerX} ${centerY} Q ${leftControlX} ${leftControlY} ${leftLobeX} ${leftLobeY} Q ${tipX} ${tipY} ${notchX} ${notchY} Q ${tipX} ${tipY} ${rightLobeX} ${rightLobeY} Q ${rightControlX} ${rightControlY} ${centerX} ${centerY} Z`;
}
case 'tulip': {
// Tulip-shaped petal - cupped shape, narrow base widening to rounded top
const cupWidth = petalWidth * 0.7;
const cupPoint = petalLength * 0.6;
// Control points for the cup shape - smooth S-curves
const leftCupX = centerX + Math.cos(rad) * cupPoint + Math.cos(perpAngle) * cupWidth;
const leftCupY = centerY + Math.sin(rad) * cupPoint + Math.sin(perpAngle) * cupWidth;
const rightCupX = centerX + Math.cos(rad) * cupPoint - Math.cos(perpAngle) * cupWidth;
const rightCupY = centerY + Math.sin(rad) * cupPoint - Math.sin(perpAngle) * cupWidth;
// Rounded top edge
const tipLeftX = tipX + Math.cos(perpAngle) * cupWidth * 0.5;
const tipLeftY = tipY + Math.sin(perpAngle) * cupWidth * 0.5;
const tipRightX = tipX - Math.cos(perpAngle) * cupWidth * 0.5;
const tipRightY = tipY - Math.sin(perpAngle) * cupWidth * 0.5;
// Smooth cubic bezier for elegant tulip shape
return `M ${centerX} ${centerY} C ${leftControlX * 0.2 + centerX * 0.8} ${leftControlY * 0.2 + centerY * 0.8} ${leftCupX} ${leftCupY} ${tipLeftX} ${tipLeftY} Q ${tipX} ${tipY} ${tipRightX} ${tipRightY} C ${rightCupX} ${rightCupY} ${rightControlX * 0.2 + centerX * 0.8} ${rightControlY * 0.2 + centerY * 0.8} ${centerX} ${centerY} Z`;
}
}
}
/**
* Generate detailed center based on style (Proposal 4)
*/
function generateCenter(
style: FlowerParams['centerStyle'],
centerX: number,
centerY: number,
size: number,
stamenCount: number,
centerColor: string,
stamenTipColor: string,
rng: () => number
): string[] {
const elements: string[] = [];
const radius = size * 0.4;
switch (style) {
case 'simple': {
// Basic circle
elements.push(`
No DID provided
'; return; } // Use the shared flower generation function const svgString = generateFlowerSVGString(this.did, this.displaySize); const showInfo = this.hasAttribute('show-info'); this.innerHTML = `