|
82 | 82 | /// - ccw (boolean): If true, steps are laid out counter-clockwise. If false, they're placed clockwise. The center of the cycle is always placed at (0, 0) |
83 | 83 | /// - radius (number, length): The radius of the cycle |
84 | 84 | /// - offset-angle (angle): Offset of the starting angle |
| 85 | +/// - step-angles (none,angle,array): Angles between the steps. |
| 86 | +/// - none: Steps are spaced evenly. |
| 87 | +/// - angle: Space between the steps, with the last angle (between the last step and the first one) completing the full circle. |
| 88 | +/// - array: An array of angles between the steps. For n steps, the array must contain n-1 angles. The last angle is automatically computed to complete the full circle. |
85 | 89 | #let basic( |
86 | 90 | steps, |
87 | 91 | arrow-style: auto, |
|
92 | 96 | radius: 2, |
93 | 97 | offset-angle: 0deg, |
94 | 98 | name: none, |
95 | | - ..style |
| 99 | + step-angles: none, |
| 100 | + ..style, |
96 | 101 | ) = { |
97 | 102 | draw.group(name: name, ctx => { |
98 | 103 | draw.anchor("default", (0, 0)) |
|
111 | 116 | let ( |
112 | 117 | sizes, |
113 | 118 | largest-width, |
114 | | - highest-height |
| 119 | + highest-height, |
115 | 120 | ) = _get-steps-sizes(steps, ctx, style, step-style-at) |
116 | 121 |
|
117 | | - let angle-step = 360deg / n-steps |
| 122 | + let step-angles = if step-angles == none { |
| 123 | + steps.map(_ => 360deg / n-steps) |
| 124 | + } else if type(step-angles) == angle { |
| 125 | + assert( |
| 126 | + step-angles >= 0deg, |
| 127 | + message: "step-angles must be positive, use the ccw parameter to change the direction" |
| 128 | + ) |
| 129 | + assert( |
| 130 | + (steps.len() - 1) * step-angles <= 360deg, |
| 131 | + message: "Sum of step angles is greater than 360°" |
| 132 | + ) |
| 133 | + let angles = (step-angles,) * (steps.len() - 1) |
| 134 | + angles + (360deg - angles.sum(default: 0deg),) |
| 135 | + } else if type(step-angles) == array { |
| 136 | + assert( |
| 137 | + step-angles.len() == n-steps - 1, |
| 138 | + message: "There must be one less step angle as there are steps. Expected " + str(n-steps - 1) + ", got " + str(step-angles.len()) |
| 139 | + ) |
| 140 | + for step-angle in step-angles { |
| 141 | + assert( |
| 142 | + step-angle >= 0deg, |
| 143 | + message: "step-angles must be positive, use the ccw parameter to change the direction" |
| 144 | + ) |
| 145 | + assert( |
| 146 | + type(step-angle) == angle, |
| 147 | + message: "All values in step-angles must be angles" |
| 148 | + ) |
| 149 | + } |
| 150 | + assert( |
| 151 | + step-angles.sum(default: 0deg) <= 360deg, |
| 152 | + message: "Sum of step angles is greater than 360°" |
| 153 | + ) |
| 154 | + step-angles + (360deg - step-angles.sum(default: 0deg),) |
| 155 | + } else { |
| 156 | + panic("step-angles must be an angle, an array or none, got " + repr(type(step-angles))) |
| 157 | + } |
118 | 158 | if not ccw { |
119 | | - angle-step *= -1 |
| 159 | + step-angles = step-angles.map(x => x * -1) |
120 | 160 | } |
121 | 161 |
|
| 162 | + let angle-at = i => ( |
| 163 | + step-angles.slice(0, i) |
| 164 | + .sum(default: 0deg) |
| 165 | + + 90deg |
| 166 | + + offset-angle |
| 167 | + ) |
| 168 | + |
122 | 169 | for (i, step) in steps.enumerate() { |
123 | | - let angle = angle-step * i + 90deg + offset-angle |
124 | | - let pos = (angle, radius) |
| 170 | + let pos = (angle-at(i), radius) |
125 | 171 |
|
126 | 172 | let step-style = style.steps + step-style-at(i) |
127 | 173 | let padding = resolve-number(ctx, step-style.padding) |
|
139 | 185 | } |
140 | 186 |
|
141 | 187 | for i in range(n-steps) { |
142 | | - let angle = angle-step * i + 90deg + offset-angle |
143 | | - |
| 188 | + let angle = angle-at(i) |
144 | 189 | let arrow-style = style.arrows + arrow-style-at(i) |
145 | 190 | let arrow-stroke = arrow-style.stroke |
146 | 191 | let arrow-fill = arrow-style.fill |
|
152 | 197 | arrow-fill = gradient.linear(s1.fill, s2.fill).sample(50%) |
153 | 198 | } |
154 | 199 |
|
| 200 | + let angle-step = step-angles.at(i) |
155 | 201 | let start-angle = angle + angle-step * 0.2 |
156 | 202 | let end-angle = angle + angle-step * 0.8 |
157 | 203 |
|
|
179 | 225 | } |
180 | 226 | draw.hide(draw.arc-through( |
181 | 227 | ..pts, |
182 | | - name: "arc-" + str(i) |
| 228 | + name: "arc-" + str(i), |
183 | 229 | )) |
184 | 230 | draw.intersections( |
185 | 231 | "i-" + str(i), |
186 | 232 | "step-" + str(i), |
187 | | - "arc-" + str(i) |
| 233 | + "arc-" + str(i), |
188 | 234 | ) |
189 | 235 | draw.intersections( |
190 | 236 | "j-" + str(i), |
191 | 237 | "step-" + str(calc.rem(i + 1, n-steps)), |
192 | | - "arc-" + str(i) |
| 238 | + "arc-" + str(i), |
193 | 239 | ) |
194 | 240 |
|
195 | 241 | draw.get-ctx(ctx => { |
|
231 | 277 | (end-angle, radius), |
232 | 278 | stroke: arrow-stroke, |
233 | 279 | mark: marks, |
234 | | - name: arrow-name |
| 280 | + name: arrow-name, |
235 | 281 | ) |
236 | | - |
| 282 | + |
237 | 283 | // Thick arrow |
238 | 284 | } else { |
239 | 285 | _draw-arc-arrow( |
|
244 | 290 | arrow-fill, |
245 | 291 | arrow-stroke, |
246 | 292 | double: arrow-style.double, |
247 | | - name: arrow-name |
| 293 | + name: arrow-name, |
248 | 294 | ) |
249 | 295 | } |
250 | | - |
| 296 | + |
251 | 297 | // Straight |
252 | 298 | } else { |
253 | 299 | let p1 = (start-angle, radius) |
254 | 300 | let p2 = (end-angle, radius) |
255 | 301 | if arrow-thickness == none { |
256 | 302 | draw.line( |
257 | | - p1, p2, |
| 303 | + p1, |
| 304 | + p2, |
258 | 305 | stroke: arrow-stroke, |
259 | 306 | mark: marks, |
260 | | - name: arrow-name |
| 307 | + name: arrow-name, |
261 | 308 | ) |
262 | 309 | } else { |
263 | 310 | _draw-arrow( |
264 | | - p1, p2, |
| 311 | + p1, |
| 312 | + p2, |
265 | 313 | arrow-thickness, |
266 | 314 | arrow-fill, |
267 | 315 | arrow-stroke, |
268 | 316 | double: arrow-style.double, |
269 | | - name: arrow-name |
| 317 | + name: arrow-name, |
270 | 318 | ) |
271 | 319 | } |
272 | 320 | } |
|
0 commit comments