Skip to content

Commit 4ef8457

Browse files
committed
decoration: Add square decoration
1 parent a9cb203 commit 4ef8457

File tree

4 files changed

+92
-75
lines changed

4 files changed

+92
-75
lines changed

src/lib/decorations.typ

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#import "decorations/brace.typ": brace, brace-default-style, flat-brace, flat-brace-default-style
2-
#import "decorations/path.typ": zigzag, wave, coil
2+
#import "decorations/path.typ": zigzag, wave, coil, square

src/lib/decorations/path.typ

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
/// The following types are supported:
1919
/// - float
2020
/// - function ratio -> float (the segment ratio is given as argument)
21-
/// - array of floats (the rounded segment ratio is used as index)
21+
/// - array of floats (the rounded down segment number is used as index modulo the array length)
2222
amplitude: 1,
2323
/// Decoration start
2424
start: 0%,
@@ -63,11 +63,19 @@
6363
factor: 150%,
6464
)
6565

66+
// Square default style
67+
#let square-default-style = (
68+
..default-style,
69+
/// Midpoint factor
70+
factor: 50%,
71+
)
72+
6673
#let resolve-amplitude(amplitude, segment, num-segments) = {
74+
segment = calc.max(0, calc.min(segment, num-segments))
6775
return if type(amplitude) == function {
68-
(amplitude)(segment / (num-segments - 1) * 100%)
76+
(amplitude)(segment / num-segments * 100%)
6977
} else if type(amplitude) == array {
70-
amplitude.at(int(calc.max(0, calc.round(segment / (num-segments - 1) * (amplitude.len() - 1)))), default: 0)
78+
amplitude.at(calc.rem(int(segment), amplitude.len()), default: 0)
7179
} else {
7280
amplitude
7381
}
@@ -183,12 +191,12 @@
183191

184192
(p0, p1) = util.revert-transform(ctx.transform, p0, p1)
185193

186-
let dir = vector.sub(p1, p0)
187-
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) {
188196
style.z-up
189197
} else {
190198
style.xy-up
191-
}))
199+
})
192200

193201
pts += fn(i, p0, p1, norm)
194202
}
@@ -238,7 +246,7 @@
238246
let f = .25 - (50% - style.factor) / 50% * .25
239247
let q-dir = vector.scale(ab, f)
240248
let up = vector.scale(norm, resolve-amplitude(style.amplitude, i + .25, num-segments) / 2)
241-
let down = vector.scale(up, -resolve-amplitude(style.amplitude, i + .75, num-segments) / 2)
249+
let down = vector.scale(norm, -resolve-amplitude(style.amplitude, i + .75, num-segments) / 2)
242250

243251
let m1 = vector.add(vector.add(a, q-dir), up)
244252
let m2 = vector.add(vector.sub(b, q-dir), down)
@@ -395,7 +403,7 @@
395403
//
396404
let fn(i, a, b, norm) = {
397405
let ab = vector.sub(b, a)
398-
let up = vector.scale(norm, resolve-amplitude(style.amplitude, i + .25, num-segments) / 2)
406+
let up = vector.scale(norm, +resolve-amplitude(style.amplitude, i + .25, num-segments) / 2)
399407
let down = vector.scale(norm, -resolve-amplitude(style.amplitude, i + .75, num-segments) / 2)
400408

401409
let ma = vector.add(vector.add(a, vector.scale(ab, .25)), up)
@@ -421,3 +429,53 @@
421429
close: close,
422430
..style)
423431
})
432+
433+
///
434+
#let square(target, close: auto, name: none, ..style) = draw.get-ctx(ctx => {
435+
let style = styles.resolve(ctx, merge: style.named(),
436+
base: square-default-style, root: "square")
437+
438+
let (segments, close) = get-segments(ctx, target)
439+
let style = resolve-style(ctx, segments, style)
440+
let num-segments = style.segments
441+
let factor = calc.max(0, calc.min(style.factor / 100%, 1))
442+
443+
// Return a list of points for the catmull-rom curve
444+
//
445+
// +----+ ▲
446+
// | | │ Up
447+
// ..a....m....b.. '
448+
// | |
449+
// +----+
450+
//
451+
let fn(i, a, b, norm) = {
452+
let ab = vector.sub(b, a)
453+
let up = vector.scale(norm, +resolve-amplitude(style.amplitude, i + .25, num-segments) / 2)
454+
let down = vector.scale(norm, -resolve-amplitude(style.amplitude, i + .75, num-segments) / 2)
455+
let m = vector.add(a, vector.scale(ab, factor))
456+
457+
if not close {
458+
if i == 0 {
459+
return (a, vector.add(a, up),
460+
vector.add(m, up), vector.add(m, down),
461+
vector.add(b, down))
462+
} else if i == num-segments - 1 {
463+
return (vector.add(a, up),
464+
vector.add(m, up), vector.add(m, down),
465+
vector.add(b, down), b)
466+
}
467+
}
468+
469+
return (vector.add(a, up),
470+
vector.add(m, up), vector.add(m, down),
471+
vector.add(b, down))
472+
}
473+
474+
return draw.merge-path(
475+
finalize-path(ctx, segments, style, draw.line(
476+
.._path-effect(ctx, segments, fn, close: close, style),
477+
close: close), close: close) ,
478+
name: name,
479+
close: close,
480+
..style)
481+
})

tests/decorations/path/ref/1.png

13.8 KB
Loading

tests/decorations/path/test.typ

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,23 @@
22
#import "/src/lib.typ": *
33
#import "/tests/helper.typ": *
44

5-
#import decorations: zigzag, coil, wave
5+
#import decorations: zigzag, coil, wave, square
66

7-
#box(stroke: 2pt + red, canvas(length: 1cm, {
8-
import draw: *
9-
10-
zigzag(line((0,0), (4,0)))
11-
zigzag(line((0,1), (4,1)), amplitude: .5)
12-
zigzag(line((0,2), (4,2)), amplitude: t => { 1 - .5 * t / 50% })
13-
zigzag(line((0,3), (4,3)), amplitude: (0, 1, 0, 1, 0))
14-
}))
15-
16-
#box(stroke: 2pt + red, canvas(length: 1cm, {
17-
import draw: *
18-
19-
wave(line((0,0), (4,0)))
20-
wave(line((0,1), (4,1)), amplitude: .5)
21-
wave(line((0,2), (4,2)), amplitude: t => { 1 - .5 * t / 50% })
22-
wave(line((0,3), (4,3)), amplitude: (0, 1, 0, 1, 0))
23-
}))
24-
25-
#box(stroke: 2pt + red, canvas(length: 1cm, {
26-
import draw: *
27-
28-
coil(line((0,0), (4,0)))
29-
coil(line((0,1), (4,1)), amplitude: .5)
30-
coil(line((0,2), (4,2)), amplitude: t => { 1 - .5 * t / 50% })
31-
coil(line((0,3), (4,3)), amplitude: (0, 1, 0, 1, 0))
32-
}))
7+
#let all-fns = (zigzag, coil, wave, square)
338

34-
#box(stroke: 2pt + red, canvas(length: 1cm, {
9+
#test-case(fn => {
3510
import draw: *
3611

37-
zigzag(hobby((0,0), (4,0), (6,2)))
38-
}))
12+
fn(line((0,0), (4,0)))
13+
fn(line((0,1), (4,1)), amplitude: .5)
14+
fn(line((0,2), (4,2)), amplitude: t => { 1 - .5 * t / 50% })
15+
fn(line((0,3), (4,3)), amplitude: (0, .5, 1))
16+
}, args: all-fns)
3917

40-
#box(stroke: 2pt + red, canvas(length: 1cm, {
18+
#test-case(fn => {
4119
import draw: *
42-
43-
coil(hobby((0,0), (4,0), (6,2)))
44-
}))
45-
46-
#box(stroke: 2pt + red, canvas(length: 1cm, {
47-
import draw: *
48-
49-
wave(hobby((0,0), (4,0), (6,2)))
50-
}))
51-
52-
#box(stroke: 2pt + red, canvas(length: 1cm, {
53-
import draw: *
54-
55-
set-style(radius: .9)
56-
zigzag(circle((0,0)), amplitude: .2, segments: 20, factor: 0%)
57-
zigzag(circle((0,2)), amplitude: .2, segments: 20, factor: 50%, stroke: blue)
58-
zigzag(circle((0,4)), amplitude: .2, segments: 20, factor: 100%, stroke: red)
59-
}))
20+
fn(hobby((0,0), (4,0), (6,2)))
21+
}, args: all-fns)
6022

6123
#box(stroke: 2pt + red, canvas(length: 1cm, {
6224
import draw: *
@@ -76,33 +38,30 @@
7638
wave(circle((0,4)), amplitude: .2, segments: 20, tension: 1, stroke: red)
7739
}))
7840

79-
80-
#test-case({
41+
#box(stroke: 2pt + red, canvas(length: 1cm, {
8142
import draw: *
8243

83-
zigzag(line((0,0), (3,0)), start: 10%, stop: 90%)
84-
zigzag(line((0,2), (3,2)), start: 1, stop: 2)
85-
})
44+
set-style(radius: .9)
45+
square(circle((0,2)), amplitude: .2, segments: 20)
46+
}))
8647

87-
#test-case({
48+
#test-case(fn => {
8849
import draw: *
8950

90-
coil(line((0,0), (3,0)), start: 10%, stop: 90%)
91-
coil(line((0,2), (3,2)), start: 1, stop: 2)
92-
})
51+
fn(line((0,0), (3,0)), start: 10%, stop: 90%, amplitude: .5)
52+
fn(line((0,1), (3,1)), start: 1, stop: 2, amplitude: .5)
53+
}, args: all-fns)
9354

94-
#test-case({
55+
#test-case(fn => {
9556
import draw: *
9657

97-
wave(line((0,0), (3,0)), start: 10%, stop: 90%)
98-
wave(line((0,2), (3,2)), start: 1, stop: 2)
99-
})
58+
fn(line((0,0,-1), (0,0,1)), start: 10%, stop: 90%)
59+
}, args: all-fns)
10060

101-
#test-case({
61+
#test-case(factor => {
10262
import draw: *
103-
104-
wave(line((0,0,-1), (0,0,1)), start: 10%, stop: 90%)
105-
})
63+
square(line((0,0), (3,0)), factor: factor)
64+
}, args: (25%, 50%, 75%))
10665

10766
#test-case({
10867
import draw: *

0 commit comments

Comments
 (0)