Skip to content

Commit cef9582

Browse files
authored
matofletcher:0.1.1 (#3114)
1 parent 4bc714d commit cef9582

File tree

6 files changed

+421
-0
lines changed

6 files changed

+421
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Andrew Voynov
4+
Copyright (c) 2024 3akev
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# [Matofletcher]
2+
3+
> [!NOTE]
4+
> This is a fork of [autofletcher], which had its last update on 2024-05-20 and
5+
> has [an open and untouched PR] since 2025-05-08. While it's being
6+
> unmaintained, this fork will provide minimal necessary updates to be able to
7+
> continue using this package.
8+
9+
This small module provides functions to (sort of) abstract away manual
10+
placement of coordinates.
11+
12+
See the [manual] for usage examples.
13+
14+
## Development
15+
16+
After cloning the repository run
17+
18+
```sh
19+
just init
20+
```
21+
22+
to create a pre-commit Git hook. It will use `typstyle` and `typst` to format
23+
code and compile manual. More specifically, it will check if both actions were
24+
already done. Otherwise the hook will fail.
25+
26+
See [`.justfile`] for more details and other recipes.
27+
28+
## Credits
29+
30+
- [fletcher]
31+
- [autofletcher]
32+
33+
[Matofletcher]: https://codeberg.org/Andrew15-5/matofletcher
34+
[fletcher]: https://github.com/Jollywatt/typst-fletcher
35+
[autofletcher]: https://github.com/3akev/autofletcher
36+
[an open and untouched PR]: https://github.com/3akev/autofletcher/pull/1
37+
[manual]: ./manual.pdf
38+
[`.justfile`]: ./.justfile
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#import "@preview/fletcher:0.5.8" as fletcher: diagram, edge, node
2+
3+
4+
// math helpers
5+
#let vecadd(v1, v2) = v1.zip(v2).map(x => x.sum())
6+
7+
#let vecmult(v1, v2) = v1.zip(v2).map(x => x.product())
8+
9+
#let vecmultx(v, s) = (v.at(0) * s, v.at(1))
10+
11+
/// Calculates the relative position of a child node, like in a tree
12+
///
13+
/// Don't call this directly; instead, pass this as a parameter to `place-nodes`.
14+
///
15+
/// - i (int): The index of the child node
16+
/// - num-total (int): The total number of children
17+
#let tree-placer(i, num-total) = {
18+
let idx = i - int((num-total - 1) / 2)
19+
return (idx, 1)
20+
}
21+
22+
/// Returns a placer that places children in a circular arc
23+
///
24+
/// It appears this breaks spread, probably because it uses
25+
/// fractional coordinates. Also, don't mix it with other non-fractional
26+
/// placers. It messes up the graph
27+
///
28+
/// - start (angle, float): The starting angle of the arc
29+
/// - length (angle, float): The length of the arc
30+
/// - radius (float): The radius of the circle
31+
#let arc-placer(start, length: 2 * calc.pi, radius: 1) = {
32+
if type(start) == angle {
33+
start = start.rad()
34+
}
35+
if type(length) == angle {
36+
length = length.rad()
37+
}
38+
39+
let length = calc.clamp(length, 0, 2 * calc.pi)
40+
41+
let r = (radius, radius)
42+
43+
let circular-placer(i, num-total) = {
44+
// if it's not a full circle, we subtract one from the total number of
45+
// children cuz i is 0-indexed, but num-total is 1-indexed (sort of), so
46+
// that leaves the last "slot" unused. this is useful when it's a full
47+
// circle, but not when it's an arc
48+
if length != 2 * calc.pi and num-total > 1 {
49+
num-total = num-total - 1
50+
}
51+
let angle = start + length * i / num-total
52+
let vec = (calc.cos(angle), calc.sin(angle))
53+
return vecmult(r, vec)
54+
}
55+
56+
return circular-placer
57+
}
58+
59+
/// A pre-defined arc placer that places children in a full circle.
60+
#let circle-placer = arc-placer(0, length: 2 * calc.pi)
61+
62+
/// Returns a generic placer, where children are placed according to the given
63+
/// relative positions. If more children are present than there are positions, positions
64+
/// are repeated.
65+
///
66+
/// This is probably sufficient for most use cases.
67+
///
68+
/// - ..placements (coordinates): Relative positions to assign to children
69+
/// -> function
70+
#let placer(..placements) = {
71+
let tab = placements.pos()
72+
73+
let discrete-placer(i, num-total) = {
74+
return tab.at(calc.rem(i, tab.len()))
75+
}
76+
77+
return discrete-placer
78+
}
79+
80+
/// Calculates the positions of `num-children` children of `parent` node.
81+
///
82+
/// Returns a pair of arrays. The first array contains the coordinates of the
83+
/// children, and the second array contains the nodes partially applied with
84+
/// the calculated positions.
85+
///
86+
/// - parent (coordinates): The coordinates of the parent node
87+
/// - num-children (int): The number of children to place
88+
/// - placer (function): The function to calculate the relative positions of the children
89+
/// - spread (int): A multiplier for the x coordinate, "spreads"
90+
/// children out. Increase this for high parent nodes.
91+
/// -> (array of coordinates + array of nodes)
92+
#let place-nodes(parent, num-children, placer, spread: 1) = {
93+
let coords = ()
94+
let children = ()
95+
for i in range(0, num-children) {
96+
let rel-vec = placer(i, num-children)
97+
let rel-vec = vecmultx(rel-vec, spread)
98+
let pos = vecadd(parent, rel-vec)
99+
coords = coords + (pos,)
100+
children = children + (node.with(pos),)
101+
}
102+
return (coords, children)
103+
}
104+
105+
/// Convenience function that draws edges between a parent node and its
106+
/// children, given the coordinates of the parent and children.
107+
///
108+
/// - parent (coordinates): The coordinates of the parent node
109+
/// - children (array of coordinates): The coordinates of the children nodes
110+
/// - ..options (any): Additional options to pass to `edge`
111+
///
112+
#let edges(parent, children, ..options) = {
113+
for child in children {
114+
edge(parent, child, ..options.pos(), ..options.named())
115+
}
116+
}
66.7 KB
Binary file not shown.

0 commit comments

Comments
 (0)