如何在P5.js中生成不重叠的随机不规则形状?求算法或特殊椭圆函数
Awesome question! Those organic, distorted ellipses you're targeting are totally achievable with a custom function that mixes controlled randomness and collision checking. Here's a step-by-step solution that's flexible, easy to randomize, and guarantees no overlaps:
Instead of relying on static curveVertex() calls, we'll create a reusable generator that tweaks an ellipse's shape with random offsets (or smoother noise for more organic results) and adds collision logic to prevent overlaps.
1. Basic Distorted Ellipse Generator
This function takes core ellipse parameters (center, radii) plus controls for shape complexity and distortion. It uses curveVertex() to create a smooth, randomized closed shape every time it's called:
// p5.js example (easily adapted to Processing) function drawWeirdEllipse(x, y, rx, ry, numPoints, distortion) { beginShape(); // Add starting control point (matches last vertex for smooth closure) let startX = x + rx * cos(TWO_PI) + random(-distortion, distortion); let startY = y + ry * sin(TWO_PI) + random(-distortion, distortion); curveVertex(startX, startY); // Generate randomized points around the ellipse for (let angle = 0; angle <= TWO_PI; angle += TWO_PI / numPoints) { let dx = rx * cos(angle) + random(-distortion, distortion); let dy = ry * sin(angle) + random(-distortion, distortion); curveVertex(x + dx, y + dy); } // Add ending control point (matches first vertex) curveVertex(x + rx * cos(0) + random(-distortion, distortion), y + ry * sin(0) + random(-distortion, distortion)); endShape(); }
numPoints: Controls how smooth the shape is (more points = smoother curves)distortion: Sets the maximum random offset from the base ellipse (higher = weirder shapes)
2. Add No-Overlap Logic
To ensure shapes never overlap, we'll track all generated shapes and use a quick distance check before creating a new one. For most cases, comparing centers plus maximum radii is fast and effective:
let shapes = []; // Stores data for all generated shapes function createNonOverlappingWeirdEllipse() { let newShape; const maxAttempts = 100; // Prevent infinite loops if space is tight let attempts = 0; do { // Randomize base parameters for the new shape let x = random(width); let y = random(height); let rx = random(20, 80); let ry = random(20, 80); let distortion = random(5, 20); let numPoints = floor(random(8, 16)); newShape = {x, y, rx, ry, distortion, numPoints}; // Check for overlap with existing shapes let isOverlapping = false; for (let shape of shapes) { let minSafeDistance = max(shape.rx, shape.ry) + max(rx, ry); let actualDistance = dist(shape.x, shape.y, x, y); if (actualDistance < minSafeDistance) { isOverlapping = true; break; } } if (!isOverlapping) { shapes.push(newShape); break; } attempts++; } while (attempts < maxAttempts); // Draw the shape if we found a valid spot if (newShape) { drawWeirdEllipse(newShape.x, newShape.y, newShape.rx, newShape.ry, newShape.numPoints, newShape.distortion); } }
3. Easy Randomization
To refresh all shapes with a single action (like a mouse click), just reset the shapes array and regenerate:
function mousePressed() { background(255); shapes = []; // Generate 10 non-overlapping weird ellipses for (let i = 0; i < 10; i++) { createNonOverlappingWeirdEllipse(); } }
Bonus: Organic Shapes with Perlin Noise
For more natural, flowing distortions (instead of jittery random offsets), swap the random() calls with Perlin noise. This creates smoother, more cohesive "weird ellipses":
function drawPerlinWeirdEllipse(x, y, rx, ry, noiseScale, noiseStrength) { beginShape(); let startNoise = noise(cos(0)*noiseScale, sin(0)*noiseScale); let startOffset = map(startNoise, 0, 1, -noiseStrength, noiseStrength); curveVertex(x + (rx + startOffset)*cos(0), y + (ry + startOffset)*sin(0)); for (let angle = 0; angle <= TWO_PI; angle += TWO_PI / 16) { let noiseVal = noise(cos(angle)*noiseScale, sin(angle)*noiseScale); let offset = map(noiseVal, 0, 1, -noiseStrength, noiseStrength); curveVertex(x + (rx + offset)*cos(angle), y + (ry + offset)*sin(angle)); } curveVertex(x + (rx + startOffset)*cos(0), y + (ry + startOffset)*sin(0)); endShape(); }
内容的提问来源于stack exchange,提问作者Julien Robitaille-Brisson

