///|
/// Parameters shared by all procedural motif generators.
pub(all) struct GeneratorOptions {
center : Vec2
radius : Double
count : Int
phase : Double
jitter : Double
seed : UInt
} derive(Eq, Debug, ToJson)
///|
pub fn GeneratorOptions::new(
center : Vec2,
radius : Double,
count : Int,
) -> GeneratorOptions {
{ center, radius, count, phase: 0.0, jitter: 0.0, seed: 1U }
}
///|
pub fn GeneratorOptions::with_phase(
self : GeneratorOptions,
phase : Double,
) -> GeneratorOptions {
{ ..self, phase, }
}
///|
pub fn GeneratorOptions::with_jitter(
self : GeneratorOptions,
jitter : Double,
seed : UInt,
) -> GeneratorOptions {
{ ..self, jitter, seed }
}
///|
/// Generates a radial ring of points, optionally using deterministic jitter.
pub fn radial_points(options : GeneratorOptions) -> Array[Vec2] {
let points = []
let random = Random::new(options.seed)
if options.count > 0 {
for i in 0.. Path {
let path = Path::new()
if options.count > 1 {
let total = options.count * 2
let points = []
for i in 0.. Path {
let path = Path::new()
if options.count > 0 {
let step = 2.0 * @math.PI / options.count.to_double()
let first = {
x: options.center.x + @math.cos(options.phase) * inner_radius,
y: options.center.y + @math.sin(options.phase) * inner_radius,
}
let _ = path.move_to(first.x, first.y)
for i in 0.. Path {
let path = Path::new()
if waves > 0 {
let steps = waves * 8
let dx = width / steps.to_double()
let _ = path.move_to(x, y + height / 2.0)
for i in 1..<=steps {
let px = x + i.to_double() * dx
let py = y +
height / 2.0 +
@math.sin(phase + i.to_double() * 2.0 * @math.PI / 8.0) * amplitude
let _ = path.line_to(px, py)
}
for reverse in 1..<=steps {
let i = steps - reverse
let px = x + i.to_double() * dx
let py = y +
height / 2.0 +
@math.sin(phase + i.to_double() * 2.0 * @math.PI / 8.0) * amplitude +
height / 3.0
let _ = path.line_to(px, py)
}
let _ = path.close()
}
path
}
///|
/// Generates concentric rings with a fixed number of samples per ring.
pub fn concentric_rings(
center : Vec2,
first_radius : Double,
gap : Double,
rings : Int,
samples : Int,
phase? : Double = 0.0,
) -> Array[Path] {
let result = []
for ring in 0.. Array[Path] {
let result = []
if cols > 0 && rows > 0 {
let sx = width / cols.to_double()
let sy = height / rows.to_double()
for row in 0.. Array[Rect] {
let result = []
let random = Random::new(seed)
if columns > 0 && rows > 0 {
let cell_width = width / columns.to_double()
let cell_height = height / rows.to_double()
for row in 0..