Skip to content

Commit 1d1f8ad

Browse files
committed
WIP modifiers II
1 parent e05ebcb commit 1d1f8ad

File tree

8 files changed

+231
-44
lines changed

8 files changed

+231
-44
lines changed

src/canvas.typ

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#import "aabb.typ"
66
#import "styles.typ"
77
#import "process.typ"
8+
#import "modifiers.typ"
89

910
#import util: typst-length
1011

@@ -65,6 +66,10 @@
6566
zigzag: zigzag,
6667
coil: coil,
6768
),
69+
path-modifiers: (
70+
linearize: modifiers.linearize,
71+
wave: modifiers.wave,
72+
),
6873
)
6974

7075
let (ctx, bounds, drawables) = process.many(ctx, body)

src/draw.typ

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#import "draw/grouping.typ": intersections, group, anchor, copy-anchors, place-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, place-marks, hide
1+
#import "draw/grouping.typ": intersections, group, anchor, copy-anchors, place-anchors, set-ctx, get-ctx, for-each-anchor, on-layer, place-marks, hide, apply-modifier
22
#import "draw/transformations.typ": set-transform, rotate, translate, scale, set-origin, move-to, set-viewport
33
#import "draw/styling.typ": set-style, fill, stroke
44
#import "draw/shapes.typ": circle, circle-through, arc, arc-through, mark, line, grid, content, rect, bezier, bezier-through, catmull, hobby, merge-path

src/draw/grouping.typ

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#import "/src/aabb.typ"
1010
#import "/src/anchor.typ" as anchor_
1111
#import "/src/deps.typ"
12+
#import "/src/modifier.typ": apply-path-modifier
1213
#import deps.oxifmt: strfmt
1314

1415
#import "transformations.typ": move-to
@@ -493,6 +494,34 @@
493494
},)
494495
}
495496

497+
/// Apply one or more element modifiers
498+
///
499+
/// - modifier (string,function): Modifier name or function
500+
/// - body (element):
501+
/// - ..style (style):
502+
#let apply-modifier(modifier, body, close: false, ..style) = {
503+
assert.eq(style.pos(), (),
504+
message: "Unexpected positional argumnets.")
505+
506+
if type(modifier) != array {
507+
modifier = (modifier,)
508+
}
509+
510+
(ctx => {
511+
let (ctx, drawables, ..) = process.many(ctx, util.resolve-body(ctx, body))
512+
513+
let style = styles.resolve(ctx.style, merge: style.named())
514+
style.decoration = modifier
515+
516+
drawables = apply-path-modifier(ctx, style, drawables, close)
517+
518+
return (
519+
ctx: ctx,
520+
drawables: drawables,
521+
)
522+
},)
523+
}
524+
496525
// DEPRECATED TODO: Remove
497526
#let place-anchors(path, name, ..anchors) = {
498527
panic("place-anchors got removed. Use path anchors `(name: <element>, anchor: <number, ratio>)` instead.")

src/draw/shapes.typ

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#import "/src/mark.typ" as mark_
1616
#import "/src/mark-shapes.typ" as mark-shapes_
1717
#import "/src/aabb.typ"
18+
#import "/src/modifier.typ"
1819

1920
#import "transformations.typ": *
2021
#import "styling.typ": *
@@ -38,25 +39,8 @@
3839
}
3940

4041
// Apply path decoration to drawables
41-
#let _apply-decoration(ctx, style, drawables) = {
42-
let deco = style.at("decoration", default: none)
43-
44-
if deco != none {
45-
import "/src/lib/decorations.typ": wave, zigzag, coil
46-
assert(type(deco) in (str, function),
47-
message: "Decoration must be of type string or function")
48-
49-
let fn = if type(deco) == str {
50-
ctx.decorations.at(deco)
51-
} else if type(deco) == function {
52-
deco
53-
}
54-
55-
assert(fn != none,
56-
message: "Decoration function is none")
57-
return _apply-decoration-fn(ctx, style, drawables, fn)
58-
}
59-
return drawables
42+
#let _apply-decoration(ctx, style, drawables, close) = {
43+
return modifier.apply-path-modifier(ctx, style, drawables, close).first()
6044
}
6145

6246
/// Draws a circle or ellipse.
@@ -102,7 +86,7 @@
10286
stroke: style.stroke
10387
)
10488

105-
drawables = _apply-decoration(ctx, style, drawables)
89+
drawables = _apply-decoration(ctx, style, drawables, true)
10690

10791
let (transform, anchors) = anchor_.setup(
10892
(_) => pos,
@@ -177,7 +161,7 @@
177161
stroke: style.stroke
178162
)
179163

180-
drawables = _apply-decoration(ctx, style, drawables)
164+
drawables = _apply-decoration(ctx, style, drawables, true)
181165

182166
let (transform, anchors) = anchor_.setup(
183167
(anchor) => (
@@ -296,7 +280,7 @@
296280
mode: style.mode
297281
)
298282

299-
drawables = _apply-decoration(ctx, style, drawables)
283+
drawables = _apply-decoration(ctx, style, drawables, false)
300284

301285
let sector-center = (
302286
x - rx * calc.cos(start-angle),
@@ -599,7 +583,7 @@
599583
)
600584

601585
// Apply decoration
602-
drawables = _apply-decoration(ctx, style, drawables)
586+
drawables = _apply-decoration(ctx, style, drawables, close)
603587

604588
// Get bounds
605589
let (transform, anchors) = anchor_.setup(
@@ -916,7 +900,7 @@
916900

917901
let drawables = ()
918902
if style.frame != none {
919-
border = _apply-decoration(ctx, style, border)
903+
border = _apply-decoration(ctx, style, border, true)
920904
drawables.push(border)
921905
}
922906

@@ -1146,7 +1130,7 @@
11461130
drawable.path(segments, fill: style.fill, stroke: style.stroke, close: true)
11471131
}
11481132

1149-
drawables = _apply-decoration(ctx, style, drawables)
1133+
drawables = _apply-decoration(ctx, style, drawables, true)
11501134

11511135
// Calculate border anchors
11521136
let center = vector.lerp(a, b, .5)
@@ -1231,7 +1215,7 @@
12311215
stroke: style.stroke,
12321216
)
12331217

1234-
drawables = _apply-decoration(ctx, style, drawables)
1218+
drawables = _apply-decoration(ctx, style, drawables, false)
12351219

12361220
let (transform, anchors) = anchor_.setup(
12371221
anchor => (
@@ -1336,7 +1320,7 @@
13361320
stroke: style.stroke,
13371321
close: close)
13381322

1339-
drawables = _apply-decoration(ctx, style, drawables)
1323+
drawables = _apply-decoration(ctx, style, drawables, close)
13401324

13411325
let (transform, anchors) = {
13421326
let a = for (i, pt) in pts.enumerate() {
@@ -1417,7 +1401,7 @@
14171401
stroke: style.stroke,
14181402
close: close)
14191403

1420-
drawables = _apply-decoration(ctx, style, drawables)
1404+
drawables = _apply-decoration(ctx, style, drawables, close)
14211405

14221406
let (transform, anchors) = {
14231407
let a = for (i, pt) in pts.enumerate() {
@@ -1509,7 +1493,7 @@
15091493
let style = styles.resolve(ctx.style, merge: style)
15101494
let drawables = drawable.path(fill: style.fill, stroke: style.stroke, close: close, segments)
15111495

1512-
drawables = _apply-decoration(ctx, style, drawables)
1496+
drawables = _apply-decoration(ctx, style, drawables, close)
15131497

15141498
let (transform, anchors) = anchor_.setup(
15151499
auto,

src/draw/transformations.typ

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#import "/src/vector.typ"
44
#import "/src/util.typ"
55

6+
67
// Utility for applying translation to and from
78
// the origin to apply a transformation matrix to.
89
//

src/modifier.typ

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,42 @@
1+
#import "/src/path-util.typ"
2+
13
/// A path modifier is a function that accepts a contex, style and
24
/// a single drawable and returns a single (modifierd) drawable.
35

6+
// Function for slicing a path into three parts,
7+
// a head, a mid section and a tail.
8+
#let slice-segments(segments, start, end) = {
9+
let len = path-util.length(segments)
10+
if type(start) == ratio {
11+
start = len * start / 100%
12+
}
13+
if type(end) == ratio {
14+
end = len * end / 100%
15+
}
16+
17+
let (head, mid, tail) = ((), segments, ())
18+
19+
if start != 0 or end != len {
20+
mid = path-util.shorten-path(segments, start, end)
21+
}
22+
23+
if start != 0 {
24+
head = path-util.shorten-path(segments, 0, len - start)
25+
}
26+
27+
if end != len {
28+
tail = path-util.shorten-path(segments, len - end, 0)
29+
}
30+
31+
return (head, mid, tail)
32+
}
33+
434
/// Apply a path modifier to a list of drawables
535
///
636
/// - ctx (context):
737
/// - style (style):
8-
/// - drawables (array): List of drawables (paths)
9-
#let apply-modifier-fn(ctx, style, drawables, fn) = {
38+
/// - elem (element): Single element
39+
#let apply-modifier-fn(ctx, style, elem, name-or-fn, close) = {
1040
let fn = if type(name-or-fn) == function {
1141
name-or-fn
1242
} else {
@@ -16,20 +46,33 @@
1646
assert(type(fn) == function,
1747
message: "Path modifier must be of type function.")
1848

19-
return drawables.map(d => {
20-
// ...
21-
return d
22-
}).filter(none)
49+
if "segments" in elem {
50+
let (head, mid, tail) = slice-segments(elem.segments, 10%, 30%)
51+
let close = close and head == () and tail == ()
52+
elem.segments = head + fn(ctx, style, mid, close) + tail
53+
}
54+
55+
return elem
2356
}
2457

2558
/// Apply a path modifier to a list of drawables
26-
#let apply-path-modifier(ctx, style, drawables) = {
59+
#let apply-path-modifier(ctx, style, drawables, close) = {
60+
if type(drawables) != array {
61+
drawables = (drawables,)
62+
}
63+
2764
let fns = if type(style.decoration) == array {
2865
style.decoration
2966
} else {
3067
(style.decoration,)
3168
}.map(n => {
32-
if type(n) == str {
69+
let name = if type(n) == dictionary {
70+
n.name
71+
} else {
72+
n
73+
}
74+
75+
if type(name) == str {
3376
assert(n in ctx.path-modifiers,
3477
message: "Unknown path-modifier: " + repr(n))
3578
ctx.path-modifiers.at(n)
@@ -43,8 +86,8 @@
4386

4487
// Apply function on all drawables
4588
return drawables.map(d => {
46-
for fn in fns {
47-
d = apply-modifier-fn(ctx, style, fn)
89+
for fn in fns.filter(v => v != none) {
90+
d = apply-modifier-fn(ctx, style, d, fn, close)
4891
}
4992
return d
5093
})

0 commit comments

Comments
 (0)