|
14 | 14 | /// Length of a single segments
|
15 | 15 | segment-length: none,
|
16 | 16 |
|
17 |
| - /// Amplitude of a segment in the direction of the segments normal |
| 17 | + /// Amplitude of a segment in the direction of the segments normal. |
| 18 | + /// The following types are supported: |
| 19 | + /// - float |
| 20 | + /// - function ratio -> float (the segment ratio is given as argument) |
| 21 | + /// - array of floats (the rounded down segment number is used as index modulo the array length) |
18 | 22 | amplitude: 1,
|
19 | 23 | /// Decoration start
|
20 | 24 | start: 0%,
|
|
59 | 63 | factor: 150%,
|
60 | 64 | )
|
61 | 65 |
|
| 66 | +// Square default style |
| 67 | +#let square-default-style = ( |
| 68 | + ..default-style, |
| 69 | + /// Midpoint factor |
| 70 | + factor: 50%, |
| 71 | +) |
| 72 | + |
| 73 | +#let resolve-amplitude(amplitude, segment, num-segments) = { |
| 74 | + segment = calc.max(0, calc.min(segment, num-segments)) |
| 75 | + return if type(amplitude) == function { |
| 76 | + (amplitude)(segment / num-segments * 100%) |
| 77 | + } else if type(amplitude) == array { |
| 78 | + amplitude.at(calc.rem(int(2*segment), amplitude.len()), default: 0) |
| 79 | + } else { |
| 80 | + amplitude |
| 81 | + } |
| 82 | +} |
| 83 | + |
62 | 84 | #let resolve-style(ctx, segments, style) = {
|
63 | 85 | assert(not (style.segments == none and style.segment-length == none),
|
64 | 86 | message: "Only one of segments or segment-length must be set, while the other must be auto")
|
|
169 | 191 |
|
170 | 192 | (p0, p1) = util.revert-transform(ctx.transform, p0, p1)
|
171 | 193 |
|
172 |
| - let dir = vector.sub(p1, p0) |
173 |
| - let norm = vector.norm(vector.cross(dir, if p0.at(2) != p1.at(2) { |
| 194 | + let dir = vector.norm(vector.sub(p1, p0)) |
| 195 | + let norm = vector.cross(dir, if p0.at(2) != p1.at(2) { |
174 | 196 | style.z-up
|
175 | 197 | } else {
|
176 | 198 | style.xy-up
|
177 |
| - })) |
| 199 | + }) |
178 | 200 |
|
179 | 201 | pts += fn(i, p0, p1, norm)
|
180 | 202 | }
|
|
221 | 243 | // list of points for the line-strip.
|
222 | 244 | let fn(i, a, b, norm) = {
|
223 | 245 | let ab = vector.sub(b, a)
|
224 |
| - |
225 | 246 | let f = .25 - (50% - style.factor) / 50% * .25
|
226 | 247 | let q-dir = vector.scale(ab, f)
|
227 |
| - let up = vector.scale(norm, style.amplitude / 2) |
228 |
| - let down = vector.scale(up, -1) |
| 248 | + let up = vector.scale(norm, resolve-amplitude(style.amplitude, i + .25, num-segments) / 2) |
| 249 | + let down = vector.scale(norm, -resolve-amplitude(style.amplitude, i + .75, num-segments) / 2) |
229 | 250 |
|
230 | 251 | let m1 = vector.add(vector.add(a, q-dir), up)
|
231 | 252 | let m2 = vector.add(vector.sub(b, q-dir), down)
|
|
301 | 322 | //
|
302 | 323 | let fn(i, a, b, norm) = {
|
303 | 324 | let ab = vector.sub(b, a)
|
304 |
| - let up = vector.scale(norm, style.amplitude / 2) |
| 325 | + let amplitude = resolve-amplitude(style.amplitude, i, num-segments) |
| 326 | + let up = vector.scale(norm, amplitude / 2) |
305 | 327 | let dist = vector.dist(a, b)
|
306 | 328 |
|
307 | 329 | let d = vector.norm(ab)
|
|
381 | 403 | //
|
382 | 404 | let fn(i, a, b, norm) = {
|
383 | 405 | let ab = vector.sub(b, a)
|
384 |
| - let up = vector.scale(norm, style.amplitude / 2) |
385 |
| - let down = vector.scale( |
386 |
| - up, -1) |
| 406 | + let up = vector.scale(norm, +resolve-amplitude(style.amplitude, i + .25, num-segments) / 2) |
| 407 | + let down = vector.scale(norm, -resolve-amplitude(style.amplitude, i + .75, num-segments) / 2) |
387 | 408 |
|
388 | 409 | let ma = vector.add(vector.add(a, vector.scale(ab, .25)), up)
|
389 | 410 | let m = vector.add(a, vector.scale(ab, .50))
|
|
408 | 429 | close: close,
|
409 | 430 | ..style)
|
410 | 431 | })
|
| 432 | + |
| 433 | +/// Draw a square-wave along a path using a line-strip |
| 434 | +/// |
| 435 | +/// The number of phases can be controlled via the `segments` or `segment-length` style key, and the width via `amplitude`. |
| 436 | +/// |
| 437 | +/// ```typc example |
| 438 | +/// line((0,0), (2,1), stroke: gray) |
| 439 | +/// cetz.decorations.square(line((0,0), (2,1)), amplitude: .25, start: 10%, stop: 90%) |
| 440 | +/// ``` |
| 441 | +/// |
| 442 | +/// - target (drawable): Target path |
| 443 | +/// - close (auto,bool): Close the path |
| 444 | +/// - name (none,string): Element name |
| 445 | +/// - ..style (style): Style |
| 446 | +/// |
| 447 | +/// ## Styling |
| 448 | +/// *Root*: `squre` |
| 449 | +/// |
| 450 | +/// - factor (ratio) = 50% Square-Wave midpoint |
| 451 | +#let square(target, close: auto, name: none, ..style) = draw.get-ctx(ctx => { |
| 452 | + let style = styles.resolve(ctx, merge: style.named(), |
| 453 | + base: square-default-style, root: "square") |
| 454 | + |
| 455 | + let (segments, close) = get-segments(ctx, target) |
| 456 | + let style = resolve-style(ctx, segments, style) |
| 457 | + let num-segments = style.segments |
| 458 | + let factor = calc.max(0, calc.min(style.factor / 100%, 1)) |
| 459 | + |
| 460 | + // Return a list of points for the line-strip |
| 461 | + // |
| 462 | + // +----+ ▲ |
| 463 | + // | | │ Up |
| 464 | + // ..a....m....b.. ' |
| 465 | + // | | |
| 466 | + // +----+ |
| 467 | + // |
| 468 | + let fn(i, a, b, norm) = { |
| 469 | + let ab = vector.sub(b, a) |
| 470 | + let up = vector.scale(norm, +resolve-amplitude(style.amplitude, i + .25, num-segments) / 2) |
| 471 | + let down = vector.scale(norm, -resolve-amplitude(style.amplitude, i + .75, num-segments) / 2) |
| 472 | + let m = vector.add(a, vector.scale(ab, factor)) |
| 473 | + |
| 474 | + if not close { |
| 475 | + if i == 0 { |
| 476 | + return (a, vector.add(a, up), |
| 477 | + vector.add(m, up), vector.add(m, down), |
| 478 | + vector.add(b, down)) |
| 479 | + } else if i == num-segments - 1 { |
| 480 | + return (vector.add(a, up), |
| 481 | + vector.add(m, up), vector.add(m, down), |
| 482 | + vector.add(b, down), b) |
| 483 | + } |
| 484 | + } |
| 485 | + |
| 486 | + return (vector.add(a, up), |
| 487 | + vector.add(m, up), vector.add(m, down), |
| 488 | + vector.add(b, down)) |
| 489 | + } |
| 490 | + |
| 491 | + return draw.merge-path( |
| 492 | + finalize-path(ctx, segments, style, draw.line( |
| 493 | + .._path-effect(ctx, segments, fn, close: close, style), |
| 494 | + close: close), close: close) , |
| 495 | + name: name, |
| 496 | + close: close, |
| 497 | + ..style) |
| 498 | +}) |
0 commit comments