1use kurbo::{Arc, BezPath, Point, Rect, Shape as _, Vec2};
10use serde::{Deserialize, Serialize};
11
12mod extra;
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename_all = "camelCase")]
17pub enum FillMode {
18 Norm,
19 None,
20 Lighten,
21 LightenLess,
22 Darken,
23 DarkenLess,
24}
25
26#[derive(Clone, Debug)]
27pub struct SubPath {
28 pub path: BezPath,
29 pub fill: FillMode,
30 pub stroke: bool,
31 pub even_odd: bool,
33}
34
35#[derive(Clone, Debug, PartialEq)]
38pub struct Handle {
39 pub index: usize,
40 pub pos: Point,
41 pub origin: Point,
42 pub dir: Vec2,
43 pub min: f64,
44 pub max: f64,
45}
46
47impl Handle {
48 fn new(index: usize, origin: Point, dir: Vec2, value: f64, min: f64, max: f64) -> Self {
49 Handle {
50 index,
51 pos: origin + dir * value,
52 origin,
53 dir,
54 min,
55 max,
56 }
57 }
58 pub fn value_at(&self, p: Point) -> f64 {
60 let l2 = self.dir.hypot2();
61 if l2 < 1e-18 {
62 return self.min;
63 }
64 let v = (p - self.origin).dot(self.dir) / l2;
65 if !v.is_finite() {
66 return self.min;
67 }
68 v.clamp(self.min.min(self.max), self.max.max(self.min))
69 .round()
70 }
71}
72
73#[derive(Clone, Debug)]
74pub struct Geometry {
75 pub paths: Vec<SubPath>,
76 pub text_rect: Rect,
78 pub handles: Vec<Handle>,
79 pub sites: Vec<Point>,
81}
82
83impl Geometry {
84 pub fn outline(&self) -> BezPath {
86 let mut p = BezPath::new();
87 for s in &self.paths {
88 p.extend(s.path.iter());
89 }
90 p
91 }
92 pub fn is_open(&self) -> bool {
94 self.paths.iter().all(|s| s.fill == FillMode::None)
95 }
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub enum Category {
102 Lines,
103 Rectangles,
104 BasicShapes,
105 BlockArrows,
106 EquationShapes,
107 Flowchart,
108 StarsAndBanners,
109 Callouts,
110 ActionButtons,
111}
112
113impl Category {
114 pub const ALL: [Category; 9] = [
115 Category::Lines,
116 Category::Rectangles,
117 Category::BasicShapes,
118 Category::BlockArrows,
119 Category::EquationShapes,
120 Category::Flowchart,
121 Category::StarsAndBanners,
122 Category::Callouts,
123 Category::ActionButtons,
124 ];
125 pub fn label(self) -> &'static str {
126 match self {
127 Category::Lines => "Lines",
128 Category::Rectangles => "Rectangles",
129 Category::BasicShapes => "Basic Shapes",
130 Category::BlockArrows => "Block Arrows",
131 Category::EquationShapes => "Equation Shapes",
132 Category::Flowchart => "Flowchart",
133 Category::StarsAndBanners => "Stars and Banners",
134 Category::Callouts => "Callouts",
135 Category::ActionButtons => "Action Buttons",
136 }
137 }
138}
139
140pub struct PresetInfo {
141 pub name: &'static str,
142 pub label: &'static str,
143 pub category: Category,
144 pub defaults: &'static [f64],
146}
147
148macro_rules! p {
149 ($n:literal, $l:literal, $c:ident, [$($d:expr),*]) => {
150 PresetInfo { name: $n, label: $l, category: Category::$c, defaults: &[$($d as f64),*] }
151 };
152}
153
154pub static CATALOG: &[PresetInfo] = &[
156 p!("line", "Line", Lines, []),
157 p!("straightConnector1", "Arrow", Lines, []),
158 p!("bentConnector3", "Connector: Elbow", Lines, [50000]),
159 p!("curvedConnector3", "Connector: Curved", Lines, [50000]),
160 p!("bentConnector2", "Connector: Elbow (one bend)", Lines, []),
161 p!("lineInv", "Line: Rising", Lines, []),
162 p!("rect", "Rectangle", Rectangles, []),
163 p!(
164 "roundRect",
165 "Rectangle: Rounded Corners",
166 Rectangles,
167 [16667]
168 ),
169 p!(
170 "snip1Rect",
171 "Rectangle: Single Corner Snipped",
172 Rectangles,
173 [16667]
174 ),
175 p!(
176 "snip2SameRect",
177 "Rectangle: Top Corners Snipped",
178 Rectangles,
179 [16667, 0]
180 ),
181 p!(
182 "snip2DiagRect",
183 "Rectangle: Diagonal Corners Snipped",
184 Rectangles,
185 [0, 16667]
186 ),
187 p!(
188 "snipRoundRect",
189 "Rectangle: Top Corners One Rounded and One Snipped",
190 Rectangles,
191 [16667, 16667]
192 ),
193 p!(
194 "round1Rect",
195 "Rectangle: Single Corner Rounded",
196 Rectangles,
197 [16667]
198 ),
199 p!(
200 "round2SameRect",
201 "Rectangle: Top Corners Rounded",
202 Rectangles,
203 [16667, 0]
204 ),
205 p!(
206 "round2DiagRect",
207 "Rectangle: Diagonal Corners Rounded",
208 Rectangles,
209 [16667, 0]
210 ),
211 p!("textBox", "Text Box", BasicShapes, []),
212 p!("ellipse", "Oval", BasicShapes, []),
213 p!("triangle", "Isosceles Triangle", BasicShapes, [50000]),
214 p!("rtTriangle", "Right Triangle", BasicShapes, []),
215 p!("parallelogram", "Parallelogram", BasicShapes, [25000]),
216 p!("trapezoid", "Trapezoid", BasicShapes, [25000]),
217 p!(
218 "nonIsoscelesTrapezoid",
219 "Trapezoid: Uneven Sides",
220 BasicShapes,
221 [25000, 25000]
222 ),
223 p!("diamond", "Diamond", BasicShapes, []),
224 p!("pentagon", "Regular Pentagon", BasicShapes, []),
225 p!("hexagon", "Hexagon", BasicShapes, [25000]),
226 p!("heptagon", "Heptagon", BasicShapes, []),
227 p!("octagon", "Octagon", BasicShapes, [29289]),
228 p!("decagon", "Decagon", BasicShapes, []),
229 p!("dodecagon", "Dodecagon", BasicShapes, []),
230 p!("pie", "Partial Circle", BasicShapes, [0, 16200000]),
231 p!("pieWedge", "Quarter Wedge", BasicShapes, []),
232 p!("chord", "Chord", BasicShapes, [2700000, 16200000]),
233 p!("teardrop", "Teardrop", BasicShapes, [100000]),
234 p!("frame", "Frame", BasicShapes, [12500]),
235 p!("cornerTabs", "Corner Tabs: Triangles", BasicShapes, []),
236 p!("squareTabs", "Corner Tabs: Squares", BasicShapes, []),
237 p!(
238 "plaqueTabs",
239 "Corner Tabs: Quarter Circles",
240 BasicShapes,
241 []
242 ),
243 p!("halfFrame", "Half Frame", BasicShapes, [33333, 33333]),
244 p!("corner", "L-Shape", BasicShapes, [50000, 50000]),
245 p!("diagStripe", "Diagonal Stripe", BasicShapes, [50000]),
246 p!("plus", "Cross", BasicShapes, [25000]),
247 p!("plaque", "Plaque", BasicShapes, [16667]),
248 p!("can", "Cylinder", BasicShapes, [25000]),
249 p!("cube", "Cube", BasicShapes, [25000]),
250 p!("bevel", "Bevel", BasicShapes, [12500]),
251 p!("donut", "Donut", BasicShapes, [25000]),
252 p!("noSmoking", "\"Not Allowed\" Symbol", BasicShapes, [18750]),
253 p!("blockArc", "Block Arc", BasicShapes, [10800000, 0, 25000]),
254 p!("foldedCorner", "Folded Corner", BasicShapes, [16667]),
255 p!("smileyFace", "Smiley Face", BasicShapes, [4653]),
256 p!("heart", "Heart", BasicShapes, []),
257 p!("lightningBolt", "Lightning Bolt", BasicShapes, []),
258 p!("sun", "Sun", BasicShapes, [25000]),
259 p!("moon", "Moon", BasicShapes, [50000]),
260 p!("cloud", "Cloud", BasicShapes, []),
261 p!("gear6", "Gear: 6 Teeth", BasicShapes, [15000, 3526]),
262 p!("gear9", "Gear: 9 Teeth", BasicShapes, [10000, 1763]),
263 p!("funnel", "Funnel", BasicShapes, []),
264 p!("arc", "Arc", BasicShapes, [16200000, 0]),
265 p!("bracketPair", "Double Bracket", BasicShapes, [16667]),
266 p!("bracePair", "Double Brace", BasicShapes, [8333]),
267 p!("leftBracket", "Left Bracket", BasicShapes, [8333]),
268 p!("rightBracket", "Right Bracket", BasicShapes, [8333]),
269 p!("leftBrace", "Left Brace", BasicShapes, [8333, 50000]),
270 p!("rightBrace", "Right Brace", BasicShapes, [8333, 50000]),
271 p!("chartX", "Chart Marker: Cross Lines", BasicShapes, []),
272 p!("chartStar", "Chart Marker: Star Lines", BasicShapes, []),
273 p!("chartPlus", "Chart Marker: Plus Lines", BasicShapes, []),
274 p!("rightArrow", "Arrow: Right", BlockArrows, [50000, 50000]),
275 p!("leftArrow", "Arrow: Left", BlockArrows, [50000, 50000]),
276 p!("upArrow", "Arrow: Up", BlockArrows, [50000, 50000]),
277 p!("downArrow", "Arrow: Down", BlockArrows, [50000, 50000]),
278 p!(
279 "leftRightArrow",
280 "Arrow: Left-Right",
281 BlockArrows,
282 [50000, 50000]
283 ),
284 p!("upDownArrow", "Arrow: Up-Down", BlockArrows, [50000, 50000]),
285 p!(
286 "quadArrow",
287 "Arrow: Quad",
288 BlockArrows,
289 [22500, 22500, 22500]
290 ),
291 p!(
292 "leftRightUpArrow",
293 "Arrow: Left-Right-Up",
294 BlockArrows,
295 [25000, 25000, 25000]
296 ),
297 p!(
298 "bentArrow",
299 "Arrow: Bent",
300 BlockArrows,
301 [25000, 25000, 25000, 43750]
302 ),
303 p!(
304 "uturnArrow",
305 "Arrow: U-Turn",
306 BlockArrows,
307 [25000, 25000, 25000, 43750, 75000]
308 ),
309 p!(
310 "curvedRightArrow",
311 "Arrow: Curved Right",
312 BlockArrows,
313 [25000, 50000, 25000]
314 ),
315 p!(
316 "curvedLeftArrow",
317 "Arrow: Curved Left",
318 BlockArrows,
319 [25000, 50000, 25000]
320 ),
321 p!(
322 "curvedUpArrow",
323 "Arrow: Curved Up",
324 BlockArrows,
325 [25000, 50000, 25000]
326 ),
327 p!(
328 "curvedDownArrow",
329 "Arrow: Curved Down",
330 BlockArrows,
331 [25000, 50000, 25000]
332 ),
333 p!(
334 "leftUpArrow",
335 "Arrow: Left-Up",
336 BlockArrows,
337 [25000, 25000, 25000]
338 ),
339 p!(
340 "bentUpArrow",
341 "Arrow: Bent-Up",
342 BlockArrows,
343 [25000, 25000, 25000]
344 ),
345 p!(
346 "stripedRightArrow",
347 "Arrow: Striped Right",
348 BlockArrows,
349 [50000, 50000]
350 ),
351 p!(
352 "notchedRightArrow",
353 "Arrow: Notched Right",
354 BlockArrows,
355 [50000, 50000]
356 ),
357 p!("homePlate", "Arrow: Pentagon", BlockArrows, [50000]),
358 p!("chevron", "Arrow: Chevron", BlockArrows, [50000]),
359 p!(
360 "circularArrow",
361 "Arrow: Circular",
362 BlockArrows,
363 [12500, 1142319, 20457681, 10800000, 12500]
364 ),
365 p!(
366 "leftCircularArrow",
367 "Arrow: Circular Counter-clockwise",
368 BlockArrows,
369 [12500, -1142319, 1142319, 10800000, 12500]
370 ),
371 p!(
372 "leftRightCircularArrow",
373 "Arrow: Circular Two-headed",
374 BlockArrows,
375 [12500, 1142319, 20457681, 11942319, 12500]
376 ),
377 p!("swooshArrow", "Arrow: Swoosh", BlockArrows, [25000, 16667]),
378 p!(
379 "rightArrowCallout",
380 "Callout: Right Arrow",
381 BlockArrows,
382 [25000, 25000, 25000, 64977]
383 ),
384 p!(
385 "downArrowCallout",
386 "Callout: Down Arrow",
387 BlockArrows,
388 [25000, 25000, 25000, 64977]
389 ),
390 p!(
391 "leftArrowCallout",
392 "Callout: Left Arrow",
393 BlockArrows,
394 [25000, 25000, 25000, 64977]
395 ),
396 p!(
397 "upArrowCallout",
398 "Callout: Up Arrow",
399 BlockArrows,
400 [25000, 25000, 25000, 64977]
401 ),
402 p!(
403 "leftRightArrowCallout",
404 "Callout: Left-Right Arrow",
405 BlockArrows,
406 [25000, 25000, 25000, 48123]
407 ),
408 p!(
409 "upDownArrowCallout",
410 "Callout: Up-Down Arrow",
411 BlockArrows,
412 [25000, 25000, 25000, 48123]
413 ),
414 p!(
415 "quadArrowCallout",
416 "Callout: Quad Arrow",
417 BlockArrows,
418 [18515, 18515, 18515, 48123]
419 ),
420 p!("mathPlus", "Plus Sign", EquationShapes, [23520]),
421 p!("mathMinus", "Minus Sign", EquationShapes, [23520]),
422 p!(
423 "mathMultiply",
424 "Multiplication Sign",
425 EquationShapes,
426 [23520]
427 ),
428 p!(
429 "mathDivide",
430 "Division Sign",
431 EquationShapes,
432 [23520, 5880, 11760]
433 ),
434 p!("mathEqual", "Equals", EquationShapes, [23520, 11760]),
435 p!(
436 "mathNotEqual",
437 "Not Equal",
438 EquationShapes,
439 [23520, 6600000, 11760]
440 ),
441 p!("flowChartProcess", "Flowchart: Process", Flowchart, []),
442 p!(
443 "flowChartAlternateProcess",
444 "Flowchart: Alternate Process",
445 Flowchart,
446 []
447 ),
448 p!("flowChartDecision", "Flowchart: Decision", Flowchart, []),
449 p!("flowChartInputOutput", "Flowchart: Data", Flowchart, []),
450 p!(
451 "flowChartPredefinedProcess",
452 "Flowchart: Predefined Process",
453 Flowchart,
454 []
455 ),
456 p!(
457 "flowChartInternalStorage",
458 "Flowchart: Internal Storage",
459 Flowchart,
460 []
461 ),
462 p!("flowChartDocument", "Flowchart: Document", Flowchart, []),
463 p!(
464 "flowChartMultidocument",
465 "Flowchart: Multidocument",
466 Flowchart,
467 []
468 ),
469 p!(
470 "flowChartTerminator",
471 "Flowchart: Terminator",
472 Flowchart,
473 []
474 ),
475 p!(
476 "flowChartPreparation",
477 "Flowchart: Preparation",
478 Flowchart,
479 []
480 ),
481 p!(
482 "flowChartManualInput",
483 "Flowchart: Manual Input",
484 Flowchart,
485 []
486 ),
487 p!(
488 "flowChartManualOperation",
489 "Flowchart: Manual Operation",
490 Flowchart,
491 []
492 ),
493 p!("flowChartConnector", "Flowchart: Connector", Flowchart, []),
494 p!(
495 "flowChartOffpageConnector",
496 "Flowchart: Off-page Connector",
497 Flowchart,
498 []
499 ),
500 p!("flowChartPunchedCard", "Flowchart: Card", Flowchart, []),
501 p!(
502 "flowChartPunchedTape",
503 "Flowchart: Punched Tape",
504 Flowchart,
505 []
506 ),
507 p!(
508 "flowChartSummingJunction",
509 "Flowchart: Summing Junction",
510 Flowchart,
511 []
512 ),
513 p!("flowChartOr", "Flowchart: Or", Flowchart, []),
514 p!("flowChartCollate", "Flowchart: Collate", Flowchart, []),
515 p!("flowChartSort", "Flowchart: Sort", Flowchart, []),
516 p!("flowChartExtract", "Flowchart: Extract", Flowchart, []),
517 p!("flowChartMerge", "Flowchart: Merge", Flowchart, []),
518 p!(
519 "flowChartOnlineStorage",
520 "Flowchart: Stored Data",
521 Flowchart,
522 []
523 ),
524 p!("flowChartDelay", "Flowchart: Delay", Flowchart, []),
525 p!(
526 "flowChartMagneticTape",
527 "Flowchart: Sequential Access Storage",
528 Flowchart,
529 []
530 ),
531 p!(
532 "flowChartMagneticDisk",
533 "Flowchart: Magnetic Disk",
534 Flowchart,
535 []
536 ),
537 p!(
538 "flowChartMagneticDrum",
539 "Flowchart: Direct Access Storage",
540 Flowchart,
541 []
542 ),
543 p!("flowChartDisplay", "Flowchart: Display", Flowchart, []),
544 p!(
545 "flowChartOfflineStorage",
546 "Flowchart: Offline Storage",
547 Flowchart,
548 []
549 ),
550 p!("irregularSeal1", "Explosion: 8 Points", StarsAndBanners, []),
551 p!(
552 "irregularSeal2",
553 "Explosion: 14 Points",
554 StarsAndBanners,
555 []
556 ),
557 p!("star4", "Star: 4 Points", StarsAndBanners, [12500]),
558 p!("star5", "Star: 5 Points", StarsAndBanners, [19098]),
559 p!("star6", "Star: 6 Points", StarsAndBanners, [28868]),
560 p!("star7", "Star: 7 Points", StarsAndBanners, [34601]),
561 p!("star8", "Star: 8 Points", StarsAndBanners, [37500]),
562 p!("star10", "Star: 10 Points", StarsAndBanners, [42533]),
563 p!("star12", "Star: 12 Points", StarsAndBanners, [37500]),
564 p!("star16", "Star: 16 Points", StarsAndBanners, [37500]),
565 p!("star24", "Star: 24 Points", StarsAndBanners, [37500]),
566 p!("star32", "Star: 32 Points", StarsAndBanners, [37500]),
567 p!(
568 "ribbon2",
569 "Ribbon: Tilted Up",
570 StarsAndBanners,
571 [16667, 50000]
572 ),
573 p!(
574 "ribbon",
575 "Ribbon: Tilted Down",
576 StarsAndBanners,
577 [16667, 50000]
578 ),
579 p!(
580 "ellipseRibbon2",
581 "Ribbon: Curved and Tilted Up",
582 StarsAndBanners,
583 [25000, 50000, 12500]
584 ),
585 p!(
586 "ellipseRibbon",
587 "Ribbon: Curved and Tilted Down",
588 StarsAndBanners,
589 [25000, 50000, 12500]
590 ),
591 p!(
592 "leftRightRibbon",
593 "Ribbon: Left-Right Fold",
594 StarsAndBanners,
595 [50000, 50000, 16667]
596 ),
597 p!(
598 "verticalScroll",
599 "Scroll: Vertical",
600 StarsAndBanners,
601 [12500]
602 ),
603 p!(
604 "horizontalScroll",
605 "Scroll: Horizontal",
606 StarsAndBanners,
607 [12500]
608 ),
609 p!("wave", "Wave", StarsAndBanners, [12500, 0]),
610 p!("doubleWave", "Double Wave", StarsAndBanners, [6250, 0]),
611 p!(
612 "wedgeRectCallout",
613 "Speech Bubble: Rectangle",
614 Callouts,
615 [-20833, 62500]
616 ),
617 p!(
618 "wedgeRoundRectCallout",
619 "Speech Bubble: Rectangle with Corners Rounded",
620 Callouts,
621 [-20833, 62500, 16667]
622 ),
623 p!(
624 "wedgeEllipseCallout",
625 "Speech Bubble: Oval",
626 Callouts,
627 [-20833, 62500]
628 ),
629 p!(
630 "cloudCallout",
631 "Thought Bubble: Cloud",
632 Callouts,
633 [-20833, 62500]
634 ),
635 p!(
636 "borderCallout1",
637 "Callout: Line",
638 Callouts,
639 [18750, -8333, 112500, -38333]
640 ),
641 p!(
642 "borderCallout2",
643 "Callout: Bent Line",
644 Callouts,
645 [18750, -8333, 18750, -16667, 112500, -46667]
646 ),
647 p!(
648 "borderCallout3",
649 "Callout: Double Bent Line",
650 Callouts,
651 [18750, -8333, 18750, -16667, 100000, -16667, 112963, -8333]
652 ),
653 p!(
654 "accentCallout1",
655 "Callout: Line with Accent Bar",
656 Callouts,
657 [18750, -8333, 112500, -38333]
658 ),
659 p!(
660 "accentCallout2",
661 "Callout: Bent Line with Accent Bar",
662 Callouts,
663 [18750, -8333, 18750, -16667, 112500, -46667]
664 ),
665 p!(
666 "accentCallout3",
667 "Callout: Double Bent Line with Accent Bar",
668 Callouts,
669 [18750, -8333, 18750, -16667, 100000, -16667, 112963, -8333]
670 ),
671 p!(
672 "callout1",
673 "Callout: Line with No Border",
674 Callouts,
675 [18750, -8333, 112500, -38333]
676 ),
677 p!(
678 "callout2",
679 "Callout: Bent Line with No Border",
680 Callouts,
681 [18750, -8333, 18750, -16667, 112500, -46667]
682 ),
683 p!(
684 "callout3",
685 "Callout: Double Bent Line with No Border",
686 Callouts,
687 [18750, -8333, 18750, -16667, 100000, -16667, 112963, -8333]
688 ),
689 p!(
690 "accentBorderCallout1",
691 "Callout: Line with Border and Accent Bar",
692 Callouts,
693 [18750, -8333, 112500, -38333]
694 ),
695 p!(
696 "accentBorderCallout2",
697 "Callout: Bent Line with Border and Accent Bar",
698 Callouts,
699 [18750, -8333, 18750, -16667, 112500, -46667]
700 ),
701 p!(
702 "accentBorderCallout3",
703 "Callout: Double Bent Line with Border and Accent Bar",
704 Callouts,
705 [18750, -8333, 18750, -16667, 100000, -16667, 112963, -8333]
706 ),
707 p!(
708 "actionButtonBackPrevious",
709 "Action Button: Go Back or Previous",
710 ActionButtons,
711 []
712 ),
713 p!(
714 "actionButtonForwardNext",
715 "Action Button: Go Forward or Next",
716 ActionButtons,
717 []
718 ),
719 p!(
720 "actionButtonBeginning",
721 "Action Button: Go to Beginning",
722 ActionButtons,
723 []
724 ),
725 p!(
726 "actionButtonEnd",
727 "Action Button: Go to End",
728 ActionButtons,
729 []
730 ),
731 p!(
732 "actionButtonHome",
733 "Action Button: Go Home",
734 ActionButtons,
735 []
736 ),
737 p!(
738 "actionButtonInformation",
739 "Action Button: Get Information",
740 ActionButtons,
741 []
742 ),
743 p!(
744 "actionButtonReturn",
745 "Action Button: Go Back",
746 ActionButtons,
747 []
748 ),
749 p!(
750 "actionButtonMovie",
751 "Action Button: Video",
752 ActionButtons,
753 []
754 ),
755 p!(
756 "actionButtonDocument",
757 "Action Button: Document",
758 ActionButtons,
759 []
760 ),
761 p!(
762 "actionButtonSound",
763 "Action Button: Sound",
764 ActionButtons,
765 []
766 ),
767 p!("actionButtonHelp", "Action Button: Help", ActionButtons, []),
768 p!(
769 "actionButtonBlank",
770 "Action Button: Custom",
771 ActionButtons,
772 []
773 ),
774];
775
776pub fn info(name: &str) -> Option<&'static PresetInfo> {
777 CATALOG.iter().find(|p| p.name == name)
778}
779
780pub fn is_line_like(name: &str) -> bool {
781 matches!(
782 name,
783 "line"
784 | "straightConnector1"
785 | "bentConnector2"
786 | "bentConnector3"
787 | "curvedConnector3"
788 | "arc"
789 ) || name.starts_with("bentConnector")
790 || name.starts_with("curvedConnector")
791}
792
793struct Pb {
796 p: BezPath,
797 cur: Point,
798}
799
800impl Pb {
801 fn new() -> Self {
802 Pb {
803 p: BezPath::new(),
804 cur: Point::ZERO,
805 }
806 }
807 fn m(&mut self, x: f64, y: f64) -> &mut Self {
808 self.cur = Point::new(x, y);
809 self.p.move_to(self.cur);
810 self
811 }
812 fn l(&mut self, x: f64, y: f64) -> &mut Self {
813 self.cur = Point::new(x, y);
814 self.p.line_to(self.cur);
815 self
816 }
817 fn c(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x: f64, y: f64) -> &mut Self {
818 self.cur = Point::new(x, y);
819 self.p
820 .curve_to(Point::new(x1, y1), Point::new(x2, y2), self.cur);
821 self
822 }
823 fn q(&mut self, x1: f64, y1: f64, x: f64, y: f64) -> &mut Self {
824 self.cur = Point::new(x, y);
825 self.p.quad_to(Point::new(x1, y1), self.cur);
826 self
827 }
828 fn arc(&mut self, wr: f64, hr: f64, st: f64, sw: f64) -> &mut Self {
831 let (s, e) = (st.to_radians(), (st + sw).to_radians());
832 let c = Point::new(self.cur.x - wr * s.cos(), self.cur.y - hr * s.sin());
833 let a = Arc::new(c, Vec2::new(wr.abs(), hr.abs()), s, e - s, 0.0);
834 a.to_cubic_beziers(0.05, |p1, p2, p| {
835 self.p.curve_to(p1, p2, p);
836 });
837 self.cur = Point::new(c.x + wr * e.cos(), c.y + hr * e.sin());
838 self
839 }
840 fn z(&mut self) -> &mut Self {
841 self.p.close_path();
842 self
843 }
844 fn poly(&mut self, pts: &[(f64, f64)]) -> &mut Self {
845 for (i, (x, y)) in pts.iter().enumerate() {
846 if i == 0 {
847 self.m(*x, *y);
848 } else {
849 self.l(*x, *y);
850 }
851 }
852 self.z()
853 }
854 fn ellipse(&mut self, cx: f64, cy: f64, rx: f64, ry: f64) -> &mut Self {
855 self.m(cx + rx, cy);
856 self.arc(rx, ry, 0.0, 360.0);
857 self.z()
858 }
859 fn take(&mut self) -> BezPath {
860 std::mem::take(&mut self.p)
861 }
862}
863
864fn fill(p: BezPath) -> SubPath {
865 SubPath {
866 path: p,
867 fill: FillMode::Norm,
868 stroke: true,
869 even_odd: false,
870 }
871}
872fn fill_eo(p: BezPath) -> SubPath {
873 SubPath {
874 path: p,
875 fill: FillMode::Norm,
876 stroke: true,
877 even_odd: true,
878 }
879}
880fn shade(p: BezPath, m: FillMode) -> SubPath {
881 SubPath {
882 path: p,
883 fill: m,
884 stroke: true,
885 even_odd: false,
886 }
887}
888fn stroke_only(p: BezPath) -> SubPath {
889 SubPath {
890 path: p,
891 fill: FillMode::None,
892 stroke: true,
893 even_odd: false,
894 }
895}
896fn fill_only(p: BezPath) -> SubPath {
897 SubPath {
898 path: p,
899 fill: FillMode::Norm,
900 stroke: false,
901 even_odd: false,
902 }
903}
904
905fn vis(deg: f64, rx: f64, ry: f64) -> f64 {
907 let t = deg.to_radians();
908 (rx * t.sin()).atan2(ry * t.cos()).to_degrees()
909}
910
911fn polygon_regular(n: usize, w: f64, h: f64, phase: f64) -> Vec<(f64, f64)> {
912 let pts: Vec<(f64, f64)> = (0..n)
913 .map(|i| {
914 let a = (phase + 360.0 * i as f64 / n as f64).to_radians();
915 (a.cos(), a.sin())
916 })
917 .collect();
918 normalize(&pts, w, h)
919}
920
921fn normalize(pts: &[(f64, f64)], w: f64, h: f64) -> Vec<(f64, f64)> {
923 let (mut x0, mut y0, mut x1, mut y1) = (f64::MAX, f64::MAX, f64::MIN, f64::MIN);
924 for (x, y) in pts {
925 x0 = x0.min(*x);
926 y0 = y0.min(*y);
927 x1 = x1.max(*x);
928 y1 = y1.max(*y);
929 }
930 let sx = if x1 - x0 > 1e-12 { w / (x1 - x0) } else { 0.0 };
931 let sy = if y1 - y0 > 1e-12 { h / (y1 - y0) } else { 0.0 };
932 pts.iter()
933 .map(|(x, y)| ((x - x0) * sx, (y - y0) * sy))
934 .collect()
935}
936
937fn star(n: usize, w: f64, h: f64, inner: f64, normalized: bool) -> Vec<(f64, f64)> {
938 let mut pts = Vec::with_capacity(n * 2);
939 for i in 0..n * 2 {
940 let a = (-90.0 + 180.0 * i as f64 / n as f64).to_radians();
941 let r = if i % 2 == 0 { 1.0 } else { inner };
942 pts.push((r * a.cos(), r * a.sin()));
943 }
944 if normalized {
945 normalize(&pts, w, h)
946 } else {
947 pts.iter()
948 .map(|(x, y)| (w / 2.0 + x * w / 2.0, h / 2.0 + y * h / 2.0))
949 .collect()
950 }
951}
952
953#[derive(Clone, Copy)]
954enum Corner {
955 Square,
956 Round(f64),
957 Snip(f64),
958}
959
960fn corner_rect(w: f64, h: f64, c: [Corner; 4]) -> BezPath {
962 let mut b = Pb::new();
963 let size = |k: Corner| match k {
964 Corner::Square => 0.0,
965 Corner::Round(r) | Corner::Snip(r) => r.max(0.0),
966 };
967 let s0 = size(c[0]);
968 b.m(s0, 0.0);
969 let s1 = size(c[1]);
971 b.l(w - s1, 0.0);
972 match c[1] {
973 Corner::Round(r) if r > 0.0 => {
974 b.arc(r, r, 270.0, 90.0);
975 }
976 Corner::Snip(_) => {
977 b.l(w, s1);
978 }
979 _ => {}
980 }
981 let s2 = size(c[2]);
982 b.l(w, h - s2);
983 match c[2] {
984 Corner::Round(r) if r > 0.0 => {
985 b.arc(r, r, 0.0, 90.0);
986 }
987 Corner::Snip(_) => {
988 b.l(w - s2, h);
989 }
990 _ => {}
991 }
992 let s3 = size(c[3]);
993 b.l(s3, h);
994 match c[3] {
995 Corner::Round(r) if r > 0.0 => {
996 b.arc(r, r, 90.0, 90.0);
997 }
998 Corner::Snip(_) => {
999 b.l(0.0, h - s3);
1000 }
1001 _ => {}
1002 }
1003 b.l(0.0, s0);
1004 match c[0] {
1005 Corner::Round(r) if r > 0.0 => {
1006 b.arc(r, r, 180.0, 90.0);
1007 }
1008 Corner::Snip(_) => {
1009 b.l(s0, 0.0);
1010 }
1011 _ => {}
1012 }
1013 b.z();
1014 b.take()
1015}
1016
1017fn mid_sites(w: f64, h: f64) -> Vec<Point> {
1018 vec![
1019 Point::new(w / 2.0, 0.0),
1020 Point::new(0.0, h / 2.0),
1021 Point::new(w / 2.0, h),
1022 Point::new(w, h / 2.0),
1023 ]
1024}
1025
1026#[derive(Clone, Copy)]
1028enum Dir {
1029 Right,
1030 Left,
1031 Up,
1032 Down,
1033}
1034
1035fn orient(d: Dir, w: f64, h: f64, pts: &[(f64, f64)]) -> Vec<(f64, f64)> {
1037 pts.iter()
1038 .map(|&(u, v)| match d {
1039 Dir::Right => (u, v),
1040 Dir::Left => (w - u, v),
1041 Dir::Up => (v, h - u),
1042 Dir::Down => (v, u),
1043 })
1044 .collect()
1045}
1046
1047fn len_thick(d: Dir, w: f64, h: f64) -> (f64, f64) {
1048 match d {
1049 Dir::Right | Dir::Left => (w, h),
1050 Dir::Up | Dir::Down => (h, w),
1051 }
1052}
1053
1054fn arrow(d: Dir, w: f64, h: f64, a1: f64, a2: f64) -> Geometry {
1055 let (l, t) = len_thick(d, w, h);
1056 let ss = l.min(t);
1057 let a1 = a1.clamp(0.0, 100000.0) / 100000.0;
1058 let dx = (ss * a2.clamp(0.0, 100000.0 * l / ss.max(1e-9)) / 100000.0).min(l);
1059 let y1 = t / 2.0 - t * a1 / 2.0;
1060 let y2 = t / 2.0 + t * a1 / 2.0;
1061 let pts = [
1062 (0.0, y1),
1063 (l - dx, y1),
1064 (l - dx, 0.0),
1065 (l, t / 2.0),
1066 (l - dx, t),
1067 (l - dx, y2),
1068 (0.0, y2),
1069 ];
1070 let pts = orient(d, w, h, &pts);
1071 let tr = orient(
1072 d,
1073 w,
1074 h,
1075 &[(0.0, y1), (l - dx + dx * (y1 / (t / 2.0).max(1e-9)), y2)],
1076 );
1077 let mut text = Rect::from_points(tr[0], tr[1]);
1078 text = text.abs();
1079 let mut g = Geometry {
1080 paths: vec![fill(Pb::new().poly(&pts).take())],
1081 text_rect: text,
1082 handles: vec![],
1083 sites: mid_sites(w, h),
1084 };
1085 let o = orient(d, w, h, &[(l - dx, t / 2.0), (l, t / 2.0)]);
1087 let (o0, o1) = (Point::new(o[0].0, o[0].1), Point::new(o[1].0, o[1].1));
1088 let across = match d {
1089 Dir::Right | Dir::Left => Vec2::new(0.0, -t / 2.0 / 100000.0),
1090 Dir::Up | Dir::Down => Vec2::new(-t / 2.0 / 100000.0, 0.0),
1091 };
1092 let along = (o0 - o1) * (ss / 100000.0 / dx.max(1e-9));
1093 g.handles
1094 .push(Handle::new(0, o0, across, a1 * 100000.0, 0.0, 100000.0));
1095 if dx > 1e-9 {
1096 g.handles.push(Handle::new(
1097 1,
1098 o1,
1099 along,
1100 a2,
1101 0.0,
1102 100000.0 * l / ss.max(1e-9),
1103 ));
1104 }
1105 g
1106}
1107
1108fn double_arrow(vertical: bool, w: f64, h: f64, a1: f64, a2: f64) -> Geometry {
1109 let (l, t) = if vertical { (h, w) } else { (w, h) };
1110 let ss = l.min(t);
1111 let a1 = a1.clamp(0.0, 100000.0) / 100000.0;
1112 let dx = (ss * a2.max(0.0) / 100000.0).min(l / 2.0);
1113 let y1 = t / 2.0 - t * a1 / 2.0;
1114 let y2 = t / 2.0 + t * a1 / 2.0;
1115 let pts = [
1116 (0.0, t / 2.0),
1117 (dx, 0.0),
1118 (dx, y1),
1119 (l - dx, y1),
1120 (l - dx, 0.0),
1121 (l, t / 2.0),
1122 (l - dx, t),
1123 (l - dx, y2),
1124 (dx, y2),
1125 (dx, t),
1126 ];
1127 let pts = orient(if vertical { Dir::Down } else { Dir::Right }, w, h, &pts);
1128 let r = if vertical {
1129 Rect::new(y1, dx, y2, h - dx)
1130 } else {
1131 Rect::new(dx, y1, w - dx, y2)
1132 };
1133 Geometry {
1134 paths: vec![fill(Pb::new().poly(&pts).take())],
1135 text_rect: r,
1136 handles: vec![],
1137 sites: mid_sites(w, h),
1138 }
1139}
1140
1141fn simple(path: BezPath, text: Rect, w: f64, h: f64) -> Geometry {
1142 Geometry {
1143 paths: vec![fill(path)],
1144 text_rect: text,
1145 handles: vec![],
1146 sites: mid_sites(w, h),
1147 }
1148}
1149
1150pub fn build(name: &str, w: f64, h: f64, adj: &[f64]) -> Option<Geometry> {
1153 if !(w.is_finite() && h.is_finite()) {
1154 return None;
1155 }
1156 let w = w.max(0.0);
1157 let h = h.max(0.0);
1158 let defaults = info(name).map(|i| i.defaults).unwrap_or(&[]);
1159 let a = |i: usize| -> f64 {
1160 let v = adj
1161 .get(i)
1162 .copied()
1163 .or_else(|| defaults.get(i).copied())
1164 .unwrap_or(0.0);
1165 if v.is_finite() { v } else { 0.0 }
1166 };
1167 let ss = w.min(h);
1168 let full = Rect::new(0.0, 0.0, w, h);
1169 let mut b = Pb::new();
1170 let g = match name {
1171 "rect" | "textBox" | "flowChartProcess" => {
1172 simple(corner_rect(w, h, [Corner::Square; 4]), full, w, h)
1173 }
1174 "actionButtonBlank"
1175 | "actionButtonBackPrevious"
1176 | "actionButtonForwardNext"
1177 | "actionButtonBeginning"
1178 | "actionButtonEnd"
1179 | "actionButtonHome"
1180 | "actionButtonInformation"
1181 | "actionButtonReturn"
1182 | "actionButtonMovie"
1183 | "actionButtonDocument"
1184 | "actionButtonSound"
1185 | "actionButtonHelp" => action_button(name, w, h),
1186 "roundRect" => {
1187 let v = a(0).clamp(0.0, 50000.0);
1188 let r = ss * v / 100000.0;
1189 let mut g = simple(
1190 corner_rect(w, h, [Corner::Round(r); 4]),
1191 full.inset(-r * 0.29289),
1192 w,
1193 h,
1194 );
1195 g.handles.push(Handle::new(
1196 0,
1197 Point::ZERO,
1198 Vec2::new(ss / 100000.0, 0.0),
1199 v,
1200 0.0,
1201 50000.0,
1202 ));
1203 g
1204 }
1205 "flowChartAlternateProcess" => simple(
1206 corner_rect(w, h, [Corner::Round(ss / 6.0); 4]),
1207 full.inset(-ss / 6.0 * 0.29289),
1208 w,
1209 h,
1210 ),
1211 "snip1Rect" => {
1212 let v = a(0).clamp(0.0, 50000.0);
1213 let d = ss * v / 100000.0;
1214 let mut g = simple(
1215 corner_rect(
1216 w,
1217 h,
1218 [
1219 Corner::Square,
1220 Corner::Snip(d),
1221 Corner::Square,
1222 Corner::Square,
1223 ],
1224 ),
1225 Rect::new(0.0, d / 2.0, w - d / 2.0, h),
1226 w,
1227 h,
1228 );
1229 g.handles.push(Handle::new(
1230 0,
1231 Point::new(w, 0.0),
1232 Vec2::new(-ss / 100000.0, 0.0),
1233 v,
1234 0.0,
1235 50000.0,
1236 ));
1237 g
1238 }
1239 "snip2SameRect" => {
1240 let (d1, d2) = (
1241 ss * a(0).clamp(0.0, 50000.0) / 1e5,
1242 ss * a(1).clamp(0.0, 50000.0) / 1e5,
1243 );
1244 simple(
1245 corner_rect(
1246 w,
1247 h,
1248 [
1249 Corner::Snip(d1),
1250 Corner::Snip(d1),
1251 Corner::Snip(d2),
1252 Corner::Snip(d2),
1253 ],
1254 ),
1255 full.inset(-d1.max(d2) / 2.0),
1256 w,
1257 h,
1258 )
1259 }
1260 "snip2DiagRect" => {
1261 let (d1, d2) = (
1262 ss * a(0).clamp(0.0, 50000.0) / 1e5,
1263 ss * a(1).clamp(0.0, 50000.0) / 1e5,
1264 );
1265 simple(
1266 corner_rect(
1267 w,
1268 h,
1269 [
1270 Corner::Snip(d1),
1271 Corner::Snip(d2),
1272 Corner::Snip(d1),
1273 Corner::Snip(d2),
1274 ],
1275 ),
1276 full.inset(-d1.max(d2) / 2.0),
1277 w,
1278 h,
1279 )
1280 }
1281 "snipRoundRect" => {
1282 let (r, d) = (
1283 ss * a(0).clamp(0.0, 50000.0) / 1e5,
1284 ss * a(1).clamp(0.0, 50000.0) / 1e5,
1285 );
1286 simple(
1287 corner_rect(
1288 w,
1289 h,
1290 [
1291 Corner::Round(r),
1292 Corner::Snip(d),
1293 Corner::Square,
1294 Corner::Square,
1295 ],
1296 ),
1297 full.inset(-r.max(d) / 2.0),
1298 w,
1299 h,
1300 )
1301 }
1302 "round1Rect" => {
1303 let r = ss * a(0).clamp(0.0, 50000.0) / 1e5;
1304 simple(
1305 corner_rect(
1306 w,
1307 h,
1308 [
1309 Corner::Square,
1310 Corner::Round(r),
1311 Corner::Square,
1312 Corner::Square,
1313 ],
1314 ),
1315 full.inset(-r * 0.29289),
1316 w,
1317 h,
1318 )
1319 }
1320 "round2SameRect" => {
1321 let (r1, r2) = (
1322 ss * a(0).clamp(0.0, 50000.0) / 1e5,
1323 ss * a(1).clamp(0.0, 50000.0) / 1e5,
1324 );
1325 simple(
1326 corner_rect(
1327 w,
1328 h,
1329 [
1330 Corner::Round(r1),
1331 Corner::Round(r1),
1332 Corner::Round(r2),
1333 Corner::Round(r2),
1334 ],
1335 ),
1336 full.inset(-r1.max(r2) * 0.29289),
1337 w,
1338 h,
1339 )
1340 }
1341 "round2DiagRect" => {
1342 let (r1, r2) = (
1343 ss * a(0).clamp(0.0, 50000.0) / 1e5,
1344 ss * a(1).clamp(0.0, 50000.0) / 1e5,
1345 );
1346 simple(
1347 corner_rect(
1348 w,
1349 h,
1350 [
1351 Corner::Round(r1),
1352 Corner::Round(r2),
1353 Corner::Round(r1),
1354 Corner::Round(r2),
1355 ],
1356 ),
1357 full.inset(-r1.max(r2) * 0.29289),
1358 w,
1359 h,
1360 )
1361 }
1362 "ellipse" | "flowChartConnector" => {
1363 let mut g = simple(
1364 b.ellipse(w / 2.0, h / 2.0, w / 2.0, h / 2.0).take(),
1365 Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355),
1366 w,
1367 h,
1368 );
1369 g.sites = ellipse_sites(w, h);
1370 g
1371 }
1372 "triangle" | "flowChartExtract" => {
1373 let v = if name == "triangle" {
1374 a(0).clamp(0.0, 100000.0)
1375 } else {
1376 50000.0
1377 };
1378 let ax = w * v / 100000.0;
1379 let mut g = simple(
1380 b.poly(&[(0.0, h), (ax, 0.0), (w, h)]).take(),
1381 Rect::new(ax / 2.0, h / 2.0, (ax + w) / 2.0, h),
1382 w,
1383 h,
1384 );
1385 g.sites = vec![
1386 Point::new(ax, 0.0),
1387 Point::new(ax / 2.0, h / 2.0),
1388 Point::new(0.0, h),
1389 Point::new(w / 2.0, h),
1390 Point::new(w, h),
1391 Point::new((ax + w) / 2.0, h / 2.0),
1392 ];
1393 if name == "triangle" {
1394 g.handles.push(Handle::new(
1395 0,
1396 Point::ZERO,
1397 Vec2::new(w / 100000.0, 0.0),
1398 v,
1399 0.0,
1400 100000.0,
1401 ));
1402 }
1403 g
1404 }
1405 "flowChartMerge" => simple(
1406 b.poly(&[(0.0, 0.0), (w, 0.0), (w / 2.0, h)]).take(),
1407 Rect::new(w / 4.0, 0.0, w * 0.75, h / 2.0),
1408 w,
1409 h,
1410 ),
1411 "rtTriangle" => simple(
1412 b.poly(&[(0.0, 0.0), (0.0, h), (w, h)]).take(),
1413 Rect::new(w / 12.0, h * 7.0 / 12.0, w * 7.0 / 12.0, h * 11.0 / 12.0),
1414 w,
1415 h,
1416 ),
1417 "parallelogram" | "flowChartInputOutput" => {
1418 let v = if name == "parallelogram" {
1419 a(0).clamp(0.0, 100000.0 * w / ss.max(1e-9))
1420 } else {
1421 20000.0 * w / ss.max(1e-9)
1422 };
1423 let off = ss * v / 100000.0;
1424 let mut g = simple(
1425 b.poly(&[(0.0, h), (off, 0.0), (w, 0.0), (w - off, h)])
1426 .take(),
1427 Rect::new(
1428 off / 2.0 + off * 0.2,
1429 h * 0.08,
1430 w - off / 2.0 - off * 0.2,
1431 h * 0.92,
1432 ),
1433 w,
1434 h,
1435 );
1436 if name == "parallelogram" {
1437 g.handles.push(Handle::new(
1438 0,
1439 Point::ZERO,
1440 Vec2::new(ss / 100000.0, 0.0),
1441 v,
1442 0.0,
1443 100000.0 * w / ss.max(1e-9),
1444 ));
1445 }
1446 g
1447 }
1448 "trapezoid" => {
1449 let v = a(0).clamp(0.0, 50000.0 * w / ss.max(1e-9));
1450 let off = ss * v / 100000.0;
1451 let mut g = simple(
1452 b.poly(&[(0.0, h), (off, 0.0), (w - off, 0.0), (w, h)])
1453 .take(),
1454 Rect::new(off * 0.66, h * 0.2, w - off * 0.66, h),
1455 w,
1456 h,
1457 );
1458 g.handles.push(Handle::new(
1459 0,
1460 Point::ZERO,
1461 Vec2::new(ss / 100000.0, 0.0),
1462 v,
1463 0.0,
1464 50000.0 * w / ss.max(1e-9),
1465 ));
1466 g
1467 }
1468 "flowChartManualOperation" => simple(
1469 b.poly(&[(0.0, 0.0), (w, 0.0), (w * 0.8, h), (w * 0.2, h)])
1470 .take(),
1471 Rect::new(w * 0.2, 0.0, w * 0.8, h),
1472 w,
1473 h,
1474 ),
1475 "diamond" | "flowChartDecision" => simple(
1476 b.poly(&[(w / 2.0, 0.0), (w, h / 2.0), (w / 2.0, h), (0.0, h / 2.0)])
1477 .take(),
1478 Rect::new(w / 4.0, h / 4.0, w * 0.75, h * 0.75),
1479 w,
1480 h,
1481 ),
1482 "flowChartSort" => {
1483 let d = b
1484 .poly(&[(w / 2.0, 0.0), (w, h / 2.0), (w / 2.0, h), (0.0, h / 2.0)])
1485 .take();
1486 let line = Pb::new().m(0.0, h / 2.0).l(w, h / 2.0).take();
1487 Geometry {
1488 paths: vec![fill(d), stroke_only(line)],
1489 text_rect: Rect::new(w / 4.0, h / 4.0, w * 0.75, h * 0.75),
1490 handles: vec![],
1491 sites: mid_sites(w, h),
1492 }
1493 }
1494 "pentagon" => simple(
1495 b.poly(&polygon_regular(5, w, h, -90.0)).take(),
1496 Rect::new(w * 0.2, h * 0.27, w * 0.8, h * 0.9),
1497 w,
1498 h,
1499 ),
1500 "heptagon" => simple(
1501 b.poly(&polygon_regular(7, w, h, -90.0)).take(),
1502 Rect::new(w * 0.16, h * 0.2, w * 0.84, h * 0.86),
1503 w,
1504 h,
1505 ),
1506 "decagon" => simple(
1507 b.poly(&polygon_regular(10, w, h, 0.0)).take(),
1508 Rect::new(w * 0.1, h * 0.17, w * 0.9, h * 0.83),
1509 w,
1510 h,
1511 ),
1512 "dodecagon" => simple(
1513 b.poly(&polygon_regular(12, w, h, 15.0)).take(),
1514 Rect::new(w * 0.13, h * 0.13, w * 0.87, h * 0.87),
1515 w,
1516 h,
1517 ),
1518 "hexagon" | "flowChartPreparation" => {
1519 let v = if name == "hexagon" {
1520 a(0).clamp(0.0, 50000.0 * w / ss.max(1e-9))
1521 } else {
1522 20000.0 * w / ss.max(1e-9)
1523 };
1524 let off = ss * v / 100000.0;
1525 let mut g = simple(
1526 b.poly(&[
1527 (0.0, h / 2.0),
1528 (off, 0.0),
1529 (w - off, 0.0),
1530 (w, h / 2.0),
1531 (w - off, h),
1532 (off, h),
1533 ])
1534 .take(),
1535 Rect::new(off * 0.6, h * 0.14, w - off * 0.6, h * 0.86),
1536 w,
1537 h,
1538 );
1539 if name == "hexagon" {
1540 g.handles.push(Handle::new(
1541 0,
1542 Point::ZERO,
1543 Vec2::new(ss / 100000.0, 0.0),
1544 v,
1545 0.0,
1546 50000.0 * w / ss.max(1e-9),
1547 ));
1548 }
1549 g
1550 }
1551 "octagon" => {
1552 let v = a(0).clamp(0.0, 50000.0);
1553 let d = ss * v / 100000.0;
1554 let mut g = simple(
1555 b.poly(&[
1556 (d, 0.0),
1557 (w - d, 0.0),
1558 (w, d),
1559 (w, h - d),
1560 (w - d, h),
1561 (d, h),
1562 (0.0, h - d),
1563 (0.0, d),
1564 ])
1565 .take(),
1566 full.inset(-d / 2.0),
1567 w,
1568 h,
1569 );
1570 g.handles.push(Handle::new(
1571 0,
1572 Point::ZERO,
1573 Vec2::new(ss / 100000.0, 0.0),
1574 v,
1575 0.0,
1576 50000.0,
1577 ));
1578 g
1579 }
1580 "plus" => {
1581 let v = a(0).clamp(0.0, 50000.0);
1582 let d = ss * v / 100000.0;
1583 let mut g = simple(
1584 b.poly(&[
1585 (d, 0.0),
1586 (w - d, 0.0),
1587 (w - d, d),
1588 (w, d),
1589 (w, h - d),
1590 (w - d, h - d),
1591 (w - d, h),
1592 (d, h),
1593 (d, h - d),
1594 (0.0, h - d),
1595 (0.0, d),
1596 (d, d),
1597 ])
1598 .take(),
1599 Rect::new(0.0, d, w, h - d),
1600 w,
1601 h,
1602 );
1603 g.handles.push(Handle::new(
1604 0,
1605 Point::ZERO,
1606 Vec2::new(ss / 100000.0, 0.0),
1607 v,
1608 0.0,
1609 50000.0,
1610 ));
1611 g
1612 }
1613 "frame" => {
1614 let v = a(0).clamp(0.0, 50000.0);
1615 let t = ss * v / 100000.0;
1616 b.poly(&[(0.0, 0.0), (w, 0.0), (w, h), (0.0, h)]);
1617 b.poly(&[(t, t), (w - t, t), (w - t, h - t), (t, h - t)]);
1618 let mut g = Geometry {
1619 paths: vec![fill_eo(b.take())],
1620 text_rect: full.inset(-t),
1621 handles: vec![],
1622 sites: mid_sites(w, h),
1623 };
1624 g.handles.push(Handle::new(
1625 0,
1626 Point::ZERO,
1627 Vec2::new(ss / 100000.0, 0.0),
1628 v,
1629 0.0,
1630 50000.0,
1631 ));
1632 g
1633 }
1634 "halfFrame" => {
1635 let (t1, t2) = (
1636 ss * a(0).clamp(0.0, 50000.0) / 1e5,
1637 ss * a(1).clamp(0.0, 50000.0) / 1e5,
1638 );
1639 simple(
1640 b.poly(&[
1641 (0.0, 0.0),
1642 (w, 0.0),
1643 (w - t1, t1),
1644 (t2, t1),
1645 (t2, h - t2),
1646 (0.0, h),
1647 ])
1648 .take(),
1649 full,
1650 w,
1651 h,
1652 )
1653 }
1654 "corner" => {
1655 let (t1, t2) = (
1656 ss * a(0).clamp(0.0, 100000.0) / 1e5,
1657 ss * a(1).clamp(0.0, 100000.0) / 1e5,
1658 );
1659 simple(
1660 b.poly(&[
1661 (0.0, 0.0),
1662 (t2, 0.0),
1663 (t2, h - t1),
1664 (w, h - t1),
1665 (w, h),
1666 (0.0, h),
1667 ])
1668 .take(),
1669 Rect::new(0.0, h - t1, w, h),
1670 w,
1671 h,
1672 )
1673 }
1674 "diagStripe" => {
1675 let k = a(0).clamp(0.0, 100000.0) / 1e5;
1676 simple(
1677 b.poly(&[(0.0, h * k), (w * k, 0.0), (w, 0.0), (0.0, h)])
1678 .take(),
1679 Rect::new(0.0, 0.0, w / 2.0, h / 2.0),
1680 w,
1681 h,
1682 )
1683 }
1684 "plaque" => {
1685 let v = a(0).clamp(0.0, 50000.0);
1686 let r = ss * v / 100000.0;
1687 b.m(0.0, r)
1688 .arc(r, r, 90.0, -90.0)
1689 .l(w - r, 0.0)
1690 .arc(r, r, 180.0, -90.0)
1691 .l(w, h - r)
1692 .arc(r, r, 270.0, -90.0)
1693 .l(r, h)
1694 .arc(r, r, 0.0, -90.0)
1695 .z();
1696 let mut g = simple(b.take(), full.inset(-r * 0.7), w, h);
1697 g.handles.push(Handle::new(
1698 0,
1699 Point::ZERO,
1700 Vec2::new(ss / 100000.0, 0.0),
1701 v,
1702 0.0,
1703 50000.0,
1704 ));
1705 g
1706 }
1707 "can" | "flowChartMagneticDisk" => {
1708 let v = if name == "can" {
1709 a(0).clamp(0.0, 50000.0 * h / ss.max(1e-9))
1710 } else {
1711 33333.0 * h / ss.max(1e-9) / 2.0
1712 };
1713 let eh = ss * v / 100000.0;
1714 let r = eh / 2.0;
1715 let body = Pb::new()
1716 .m(0.0, r)
1717 .l(0.0, h - r)
1718 .arc(w / 2.0, r, 180.0, -180.0)
1719 .l(w, r)
1720 .arc(w / 2.0, r, 0.0, -180.0)
1721 .z()
1722 .take();
1723 let top = Pb::new().ellipse(w / 2.0, r, w / 2.0, r).take();
1724 let mut g = Geometry {
1725 paths: vec![
1726 fill(body),
1727 shade(
1728 top,
1729 if name == "can" {
1730 FillMode::Lighten
1731 } else {
1732 FillMode::Norm
1733 },
1734 ),
1735 ],
1736 text_rect: Rect::new(0.0, eh, w, h - r),
1737 handles: vec![],
1738 sites: mid_sites(w, h),
1739 };
1740 if name == "can" {
1741 g.handles.push(Handle::new(
1742 0,
1743 Point::new(w / 2.0, 0.0),
1744 Vec2::new(0.0, ss / 100000.0),
1745 v,
1746 0.0,
1747 50000.0 * h / ss.max(1e-9),
1748 ));
1749 }
1750 g
1751 }
1752 "flowChartMagneticDrum" => {
1753 let r = w / 6.0 / 2.0;
1754 let body = Pb::new()
1755 .m(r, 0.0)
1756 .l(w - r, 0.0)
1757 .arc(r, h / 2.0, 270.0, 180.0)
1758 .l(r, h)
1759 .arc(r, h / 2.0, 90.0, 180.0)
1760 .z()
1761 .take();
1762 let side = Pb::new()
1763 .m(w - r, 0.0)
1764 .arc(r, h / 2.0, 270.0, -180.0)
1765 .take();
1766 Geometry {
1767 paths: vec![fill(body), stroke_only(side)],
1768 text_rect: Rect::new(r, 0.0, w - 2.0 * r, h),
1769 handles: vec![],
1770 sites: mid_sites(w, h),
1771 }
1772 }
1773 "cube" => {
1774 let v = a(0).clamp(0.0, 100000.0);
1775 let d = ss * v / 100000.0;
1776 let front = Pb::new()
1777 .poly(&[(0.0, d), (w - d, d), (w - d, h), (0.0, h)])
1778 .take();
1779 let top = Pb::new()
1780 .poly(&[(0.0, d), (d, 0.0), (w, 0.0), (w - d, d)])
1781 .take();
1782 let side = Pb::new()
1783 .poly(&[(w - d, d), (w, 0.0), (w, h - d), (w - d, h)])
1784 .take();
1785 let mut g = Geometry {
1786 paths: vec![
1787 fill(front),
1788 shade(top, FillMode::LightenLess),
1789 shade(side, FillMode::DarkenLess),
1790 ],
1791 text_rect: Rect::new(0.0, d, w - d, h),
1792 handles: vec![],
1793 sites: mid_sites(w, h),
1794 };
1795 g.handles.push(Handle::new(
1796 0,
1797 Point::new(0.0, 0.0),
1798 Vec2::new(0.0, ss / 100000.0),
1799 v,
1800 0.0,
1801 100000.0,
1802 ));
1803 g
1804 }
1805 "bevel" => {
1806 let v = a(0).clamp(0.0, 50000.0);
1807 let d = ss * v / 100000.0;
1808 let inner = Pb::new()
1809 .poly(&[(d, d), (w - d, d), (w - d, h - d), (d, h - d)])
1810 .take();
1811 let top = Pb::new()
1812 .poly(&[(0.0, 0.0), (w, 0.0), (w - d, d), (d, d)])
1813 .take();
1814 let left = Pb::new()
1815 .poly(&[(0.0, 0.0), (d, d), (d, h - d), (0.0, h)])
1816 .take();
1817 let right = Pb::new()
1818 .poly(&[(w, 0.0), (w, h), (w - d, h - d), (w - d, d)])
1819 .take();
1820 let bottom = Pb::new()
1821 .poly(&[(0.0, h), (d, h - d), (w - d, h - d), (w, h)])
1822 .take();
1823 let mut g = Geometry {
1824 paths: vec![
1825 fill(inner),
1826 shade(top, FillMode::LightenLess),
1827 shade(left, FillMode::Lighten),
1828 shade(right, FillMode::DarkenLess),
1829 shade(bottom, FillMode::Darken),
1830 ],
1831 text_rect: full.inset(-d),
1832 handles: vec![],
1833 sites: mid_sites(w, h),
1834 };
1835 g.handles.push(Handle::new(
1836 0,
1837 Point::ZERO,
1838 Vec2::new(ss / 100000.0, 0.0),
1839 v,
1840 0.0,
1841 50000.0,
1842 ));
1843 g
1844 }
1845 "donut" => {
1846 let v = a(0).clamp(0.0, 50000.0);
1847 let t = ss * v / 100000.0;
1848 b.ellipse(w / 2.0, h / 2.0, w / 2.0, h / 2.0);
1849 b.ellipse(
1850 w / 2.0,
1851 h / 2.0,
1852 (w / 2.0 - t).max(0.0),
1853 (h / 2.0 - t).max(0.0),
1854 );
1855 let mut g = Geometry {
1856 paths: vec![fill_eo(b.take())],
1857 text_rect: Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355),
1858 handles: vec![],
1859 sites: ellipse_sites(w, h),
1860 };
1861 g.handles.push(Handle::new(
1862 0,
1863 Point::new(0.0, h / 2.0),
1864 Vec2::new(ss / 100000.0, 0.0),
1865 v,
1866 0.0,
1867 50000.0,
1868 ));
1869 g
1870 }
1871 "noSmoking" => {
1872 let v = a(0).clamp(0.0, 50000.0);
1873 let t = ss * v / 100000.0;
1874 let (rx, ry) = ((w / 2.0 - t).max(0.0), (h / 2.0 - t).max(0.0));
1875 b.ellipse(w / 2.0, h / 2.0, w / 2.0, h / 2.0);
1876 b.ellipse(w / 2.0, h / 2.0, rx, ry);
1877 let ring = b.take();
1878 let th = (h).atan2(w);
1879 let dir = Vec2::new(th.cos(), th.sin());
1880 let r = if rx > 0.0 && ry > 0.0 {
1881 rx * ry / ((ry * th.cos()).powi(2) + (rx * th.sin()).powi(2)).sqrt()
1882 } else {
1883 0.0
1884 };
1885 let perp = Vec2::new(-dir.y, dir.x) * (t / 2.0);
1886 let c = Point::new(w / 2.0, h / 2.0);
1887 let p = [
1888 c - dir * r - perp,
1889 c + dir * r - perp,
1890 c + dir * r + perp,
1891 c - dir * r + perp,
1892 ];
1893 let bar = Pb::new().poly(&p.map(|q| (q.x, q.y))).take();
1894 Geometry {
1895 paths: vec![fill_eo(ring), fill(bar)],
1896 text_rect: Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355),
1897 handles: vec![Handle::new(
1898 0,
1899 Point::new(0.0, h / 2.0),
1900 Vec2::new(ss / 100000.0, 0.0),
1901 v,
1902 0.0,
1903 50000.0,
1904 )],
1905 sites: ellipse_sites(w, h),
1906 }
1907 }
1908 "blockArc" => {
1909 let st = a(0) / 60000.0;
1910 let en = a(1) / 60000.0;
1911 let t = ss * a(2).clamp(0.0, 50000.0) / 100000.0;
1912 let mut sw = en - st;
1913 if sw <= 0.0 {
1914 sw += 360.0;
1915 }
1916 let (rx, ry) = (w / 2.0, h / 2.0);
1917 let (irx, iry) = ((rx - t).max(0.0), (ry - t).max(0.0));
1918 let s0 = vis(st, rx, ry);
1919 let e0 = vis(st + sw, rx, ry);
1920 let mut sweep = e0 - s0;
1921 if sweep <= 0.0 {
1922 sweep += 360.0;
1923 }
1924 let si = vis(st + sw, irx, iry);
1925 let ei = vis(st, irx, iry);
1926 let mut isweep = ei - si;
1927 if isweep >= 0.0 {
1928 isweep -= 360.0;
1929 }
1930 let p0 = Point::new(
1931 w / 2.0 + rx * s0.to_radians().cos(),
1932 h / 2.0 + ry * s0.to_radians().sin(),
1933 );
1934 b.m(p0.x, p0.y).arc(rx, ry, s0, sweep);
1935 let pi = Point::new(
1936 w / 2.0 + irx * si.to_radians().cos(),
1937 h / 2.0 + iry * si.to_radians().sin(),
1938 );
1939 b.l(pi.x, pi.y).arc(irx, iry, si, isweep).z();
1940 simple(b.take(), full, w, h)
1941 }
1942 "pie" | "chord" | "arc" => {
1943 let (rx, ry) = (w / 2.0, h / 2.0);
1944 let s = vis(a(0) / 60000.0, rx, ry);
1945 let e = vis(a(1) / 60000.0, rx, ry);
1946 let mut sw = e - s;
1947 if sw <= 0.0 {
1948 sw += 360.0;
1949 }
1950 let p0 = Point::new(
1951 rx + rx * s.to_radians().cos(),
1952 ry + ry * s.to_radians().sin(),
1953 );
1954 b.m(p0.x, p0.y).arc(rx, ry, s, sw);
1955 let text = Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355);
1956 match name {
1957 "pie" => {
1958 b.l(rx, ry).z();
1959 simple(b.take(), text, w, h)
1960 }
1961 "chord" => {
1962 b.z();
1963 simple(b.take(), text, w, h)
1964 }
1965 _ => {
1966 let open = b.take();
1967 let mut f = BezPath::from_vec(open.elements().to_vec());
1968 f.line_to(Point::new(rx, ry));
1969 f.close_path();
1970 Geometry {
1971 paths: vec![fill_only(f), stroke_only(open)],
1972 text_rect: text,
1973 handles: vec![],
1974 sites: mid_sites(w, h),
1975 }
1976 }
1977 }
1978 }
1979 "teardrop" => {
1980 let k = a(0).clamp(0.0, 200000.0) / 100000.0;
1981 let tip = (w / 2.0 + w / 2.0 * k, h / 2.0 - h / 2.0 * k);
1982 b.m(0.0, h / 2.0).arc(w / 2.0, h / 2.0, 180.0, 90.0);
1983 b.q(w / 2.0 + w / 4.0 * k, 0.0, tip.0, tip.1)
1984 .q(w, h / 2.0 - h / 4.0 * k, w, h / 2.0);
1985 b.arc(w / 2.0, h / 2.0, 0.0, 180.0).z();
1986 simple(
1987 b.take(),
1988 Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355),
1989 w,
1990 h,
1991 )
1992 }
1993 "foldedCorner" => {
1994 let v = a(0).clamp(0.0, 50000.0);
1995 let d = ss * v / 100000.0;
1996 let body = Pb::new()
1997 .poly(&[(0.0, 0.0), (w, 0.0), (w, h - d), (w - d, h), (0.0, h)])
1998 .take();
1999 let fold = Pb::new()
2000 .m(w - d, h)
2001 .l(w - d + d * 0.2, h - d + d * 0.2)
2002 .l(w, h - d)
2003 .z()
2004 .take();
2005 Geometry {
2006 paths: vec![fill(body), shade(fold, FillMode::DarkenLess)],
2007 text_rect: Rect::new(0.0, 0.0, w, h - d),
2008 handles: vec![],
2009 sites: mid_sites(w, h),
2010 }
2011 }
2012 "smileyFace" => {
2013 let k = a(0).clamp(-4653.0, 4653.0) / 100000.0;
2014 let face = Pb::new().ellipse(w / 2.0, h / 2.0, w / 2.0, h / 2.0).take();
2015 let mut eyes = Pb::new();
2016 eyes.ellipse(w * 0.35, h * 0.37, w * 0.05, h * 0.05);
2017 eyes.ellipse(w * 0.65, h * 0.37, w * 0.05, h * 0.05);
2018 let ys = h * (0.72 - k);
2019 let smile = Pb::new()
2020 .m(w * 0.22, ys)
2021 .q(w / 2.0, ys + h * k * 4.0 + h * 0.0, w * 0.78, ys)
2022 .take();
2023 Geometry {
2024 paths: vec![
2025 fill(face),
2026 shade(eyes.take(), FillMode::DarkenLess),
2027 stroke_only(smile),
2028 ],
2029 text_rect: Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355),
2030 handles: vec![],
2031 sites: ellipse_sites(w, h),
2032 }
2033 }
2034 "heart" => {
2035 b.m(w / 2.0, h * 0.25);
2036 b.c(w * 0.62, h * -0.02, w, h * 0.0, w, h * 0.32);
2037 b.c(w, h * 0.6, w * 0.62, h * 0.78, w / 2.0, h);
2038 b.c(w * 0.38, h * 0.78, 0.0, h * 0.6, 0.0, h * 0.32);
2039 b.c(0.0, h * 0.0, w * 0.38, h * -0.02, w / 2.0, h * 0.25);
2040 b.z();
2041 simple(
2042 b.take(),
2043 Rect::new(w * 0.2, h * 0.25, w * 0.8, h * 0.7),
2044 w,
2045 h,
2046 )
2047 }
2048 "lightningBolt" => {
2049 let pts = normalize(
2050 &[
2051 (0.36, 0.0),
2052 (0.68, 0.0),
2053 (0.53, 0.34),
2054 (0.79, 0.34),
2055 (0.24, 1.0),
2056 (0.39, 0.53),
2057 (0.15, 0.53),
2058 ],
2059 w,
2060 h,
2061 );
2062 simple(
2063 b.poly(&pts).take(),
2064 Rect::new(w * 0.3, h * 0.35, w * 0.65, h * 0.6),
2065 w,
2066 h,
2067 )
2068 }
2069 "sun" => {
2070 let k = a(0).clamp(12500.0, 46875.0) / 100000.0;
2071 let (cx, cy) = (w / 2.0, h / 2.0);
2072 let cr = 0.5 - k;
2073 let core = Pb::new()
2074 .ellipse(cx, cy, w * cr * 0.72, h * cr * 0.72)
2075 .take();
2076 let mut rays = Pb::new();
2077 for i in 0..8 {
2078 let ang = (i as f64 * 45.0).to_radians();
2079 let half = 9f64.to_radians();
2080 let rb = cr * 0.72 + 0.06;
2081 let tip = (cx + w * 0.5 * ang.cos(), cy + h * 0.5 * ang.sin());
2082 let b1 = (
2083 cx + w * rb * (ang - half).cos(),
2084 cy + h * rb * (ang - half).sin(),
2085 );
2086 let b2 = (
2087 cx + w * rb * (ang + half).cos(),
2088 cy + h * rb * (ang + half).sin(),
2089 );
2090 rays.poly(&[tip, b1, b2]);
2091 }
2092 Geometry {
2093 paths: vec![fill(rays.take()), fill(core)],
2094 text_rect: Rect::new(w * 0.3, h * 0.3, w * 0.7, h * 0.7),
2095 handles: vec![],
2096 sites: mid_sites(w, h),
2097 }
2098 }
2099 "moon" => {
2100 let k = a(0).clamp(0.0, 87500.0) / 100000.0;
2101 let g0 = w * k;
2102 b.m(w, 0.0)
2103 .arc(w, h / 2.0, 270.0, -180.0)
2104 .arc((w - g0).max(0.0), h / 2.0, 90.0, 180.0)
2105 .z();
2106 simple(
2107 b.take(),
2108 Rect::new(w * 0.1, h * 0.25, w * (1.0 - k).max(0.2) * 0.8, h * 0.75),
2109 w,
2110 h,
2111 )
2112 }
2113 "cloud" | "cloudCallout" => cloud(name, w, h, a(0), a(1)),
2114 "bracketPair" => {
2115 let r = ss * a(0).clamp(0.0, 50000.0) / 1e5;
2116 let mut l = Pb::new();
2117 l.m(r, h)
2118 .arc(r, r, 90.0, 90.0)
2119 .l(0.0, r)
2120 .arc(r, r, 180.0, 90.0);
2121 l.m(w - r, 0.0)
2122 .arc(r, r, 270.0, 90.0)
2123 .l(w, h - r)
2124 .arc(r, r, 0.0, 90.0);
2125 let f = corner_rect(w, h, [Corner::Round(r); 4]);
2126 Geometry {
2127 paths: vec![fill_only(f), stroke_only(l.take())],
2128 text_rect: full.inset(-r * 0.29289),
2129 handles: vec![],
2130 sites: mid_sites(w, h),
2131 }
2132 }
2133 "bracePair" => {
2134 let r = ss * a(0).clamp(0.0, 25000.0) / 1e5;
2135 let mut l = Pb::new();
2136 l.m(2.0 * r, h)
2137 .arc(r, r, 90.0, 90.0)
2138 .l(r, h / 2.0 + r)
2139 .arc(r, r, 0.0, -90.0)
2140 .arc(r, r, 90.0, -90.0)
2141 .l(r, r)
2142 .arc(r, r, 180.0, 90.0);
2143 l.m(w - 2.0 * r, 0.0)
2144 .arc(r, r, 270.0, 90.0)
2145 .l(w - r, h / 2.0 - r)
2146 .arc(r, r, 180.0, -90.0)
2147 .arc(r, r, 270.0, -90.0)
2148 .l(w - r, h - r)
2149 .arc(r, r, 0.0, 90.0);
2150 let f = corner_rect(w, h, [Corner::Round(r); 4]);
2151 Geometry {
2152 paths: vec![fill_only(f), stroke_only(l.take())],
2153 text_rect: full.inset(-r * 1.2),
2154 handles: vec![],
2155 sites: mid_sites(w, h),
2156 }
2157 }
2158 "leftBracket" | "rightBracket" => {
2159 let r = (ss * a(0).clamp(0.0, 50000.0 * h / ss.max(1e-9)) / 1e5).min(h / 2.0);
2160 let mut l = Pb::new();
2161 l.m(w, h)
2162 .arc(w, r, 90.0, 90.0)
2163 .l(0.0, r)
2164 .arc(w, r, 180.0, 90.0);
2165 let mut path = l.take();
2166 if name == "rightBracket" {
2167 path.apply_affine(kurbo::Affine::new([-1.0, 0.0, 0.0, 1.0, w, 0.0]));
2168 }
2169 Geometry {
2170 paths: vec![stroke_only(path)],
2171 text_rect: full,
2172 handles: vec![],
2173 sites: mid_sites(w, h),
2174 }
2175 }
2176 "leftBrace" | "rightBrace" => {
2177 let r = (ss * a(0).clamp(0.0, 25000.0 * h / ss.max(1e-9)) / 1e5).min(h / 4.0);
2178 let my = h * a(1).clamp(0.0, 100000.0) / 1e5;
2179 let hw = w / 2.0;
2180 let mut l = Pb::new();
2181 l.m(w, h)
2182 .arc(hw, r, 90.0, 90.0)
2183 .l(hw, my + r)
2184 .arc(hw, r, 0.0, -90.0)
2185 .arc(hw, r, 90.0, -90.0)
2186 .l(hw, r)
2187 .arc(hw, r, 180.0, 90.0);
2188 let mut path = l.take();
2189 if name == "rightBrace" {
2190 path.apply_affine(kurbo::Affine::new([-1.0, 0.0, 0.0, 1.0, w, 0.0]));
2191 }
2192 Geometry {
2193 paths: vec![stroke_only(path)],
2194 text_rect: full,
2195 handles: vec![],
2196 sites: mid_sites(w, h),
2197 }
2198 }
2199 "rightArrow" => arrow(Dir::Right, w, h, a(0), a(1)),
2200 "leftArrow" => arrow(Dir::Left, w, h, a(0), a(1)),
2201 "upArrow" => arrow(Dir::Up, w, h, a(0), a(1)),
2202 "downArrow" => arrow(Dir::Down, w, h, a(0), a(1)),
2203 "leftRightArrow" => double_arrow(false, w, h, a(0), a(1)),
2204 "upDownArrow" => double_arrow(true, w, h, a(0), a(1)),
2205 "notchedRightArrow" | "stripedRightArrow" => {
2206 let mut g = arrow(Dir::Right, w, h, a(0), a(1));
2207 let k = a(0).clamp(0.0, 100000.0) / 1e5;
2208 let (y1, y2) = (h / 2.0 - h * k / 2.0, h / 2.0 + h * k / 2.0);
2209 let dx = (ss * a(1).max(0.0) / 1e5).min(w);
2210 if name == "notchedRightArrow" {
2211 let n = dx * (k / 2.0) * 0.9;
2212 let pts = [
2213 (0.0, y1),
2214 (w - dx, y1),
2215 (w - dx, 0.0),
2216 (w, h / 2.0),
2217 (w - dx, h),
2218 (w - dx, y2),
2219 (0.0, y2),
2220 (n, h / 2.0),
2221 ];
2222 g.paths = vec![fill(Pb::new().poly(&pts).take())];
2223 } else {
2224 let s = ss / 32.0;
2225 let start = 5.0 * s;
2226 let pts = [
2227 (start, y1),
2228 (w - dx, y1),
2229 (w - dx, 0.0),
2230 (w, h / 2.0),
2231 (w - dx, h),
2232 (w - dx, y2),
2233 (start, y2),
2234 ];
2235 let mut stripes = Pb::new();
2236 stripes.poly(&[(0.0, y1), (s, y1), (s, y2), (0.0, y2)]);
2237 stripes.poly(&[(2.0 * s, y1), (4.0 * s, y1), (4.0 * s, y2), (2.0 * s, y2)]);
2238 g.paths = vec![fill(stripes.take()), fill(Pb::new().poly(&pts).take())];
2239 }
2240 g
2241 }
2242 "homePlate" | "chevron" => {
2243 let v = a(0).clamp(0.0, 100000.0 * w / ss.max(1e-9));
2244 let d = (ss * v / 1e5).min(w);
2245 let pts: Vec<(f64, f64)> = if name == "homePlate" {
2246 vec![(0.0, 0.0), (w - d, 0.0), (w, h / 2.0), (w - d, h), (0.0, h)]
2247 } else {
2248 vec![
2249 (0.0, 0.0),
2250 (w - d, 0.0),
2251 (w, h / 2.0),
2252 (w - d, h),
2253 (0.0, h),
2254 (d, h / 2.0),
2255 ]
2256 };
2257 let tx = if name == "homePlate" { 0.0 } else { d };
2258 let mut g = simple(
2259 b.poly(&pts).take(),
2260 Rect::new(tx, 0.0, w - d / 2.0, h),
2261 w,
2262 h,
2263 );
2264 g.handles.push(Handle::new(
2265 0,
2266 Point::new(w, 0.0),
2267 Vec2::new(-ss / 1e5, 0.0),
2268 v,
2269 0.0,
2270 100000.0 * w / ss.max(1e-9),
2271 ));
2272 g
2273 }
2274 "quadArrow" => {
2275 let (t, hw, hl) = (
2276 ss * a(0).clamp(0.0, 50000.0) / 1e5,
2277 ss * a(1).clamp(0.0, 50000.0) / 1e5,
2278 ss * a(2).clamp(0.0, 50000.0) / 1e5,
2279 );
2280 let (cx, cy) = (w / 2.0, h / 2.0);
2281 let (t2, hw2) = (t / 2.0, hw);
2282 let pts = [
2283 (0.0, cy),
2284 (hl, cy - hw2),
2285 (hl, cy - t2),
2286 (cx - t2, cy - t2),
2287 (cx - t2, hl),
2288 (cx - hw2, hl),
2289 (cx, 0.0),
2290 (cx + hw2, hl),
2291 (cx + t2, hl),
2292 (cx + t2, cy - t2),
2293 (w - hl, cy - t2),
2294 (w - hl, cy - hw2),
2295 (w, cy),
2296 (w - hl, cy + hw2),
2297 (w - hl, cy + t2),
2298 (cx + t2, cy + t2),
2299 (cx + t2, h - hl),
2300 (cx + hw2, h - hl),
2301 (cx, h),
2302 (cx - hw2, h - hl),
2303 (cx - t2, h - hl),
2304 (cx - t2, cy + t2),
2305 (hl, cy + t2),
2306 (hl, cy + hw2),
2307 ];
2308 simple(
2309 b.poly(&pts).take(),
2310 Rect::new(cx - t2, cy - t2, cx + t2, cy + t2),
2311 w,
2312 h,
2313 )
2314 }
2315 "leftRightUpArrow" => {
2316 let (t, hw, hl) = (
2317 ss * a(0).clamp(0.0, 50000.0) / 1e5,
2318 ss * a(1).clamp(0.0, 50000.0) / 1e5,
2319 ss * a(2).clamp(0.0, 50000.0) / 1e5,
2320 );
2321 let cx = w / 2.0;
2322 let by = h - hw; let pts = [
2324 (0.0, by),
2325 (hl, by - hw),
2326 (hl, by - t / 2.0),
2327 (cx - t / 2.0, by - t / 2.0),
2328 (cx - t / 2.0, hl),
2329 (cx - hw, hl),
2330 (cx, 0.0),
2331 (cx + hw, hl),
2332 (cx + t / 2.0, hl),
2333 (cx + t / 2.0, by - t / 2.0),
2334 (w - hl, by - t / 2.0),
2335 (w - hl, by - hw),
2336 (w, by),
2337 (w - hl, h),
2338 (w - hl, by + t / 2.0),
2339 (hl, by + t / 2.0),
2340 (hl, h),
2341 ];
2342 simple(
2343 b.poly(&pts).take(),
2344 Rect::new(hl, by - t / 2.0, w - hl, by + t / 2.0),
2345 w,
2346 h,
2347 )
2348 }
2349 "leftUpArrow" | "bentUpArrow" => {
2350 let (t, hw, hl) = (
2351 ss * a(0).clamp(0.0, 50000.0) / 1e5,
2352 ss * a(1).clamp(0.0, 50000.0) / 1e5,
2353 ss * a(2).clamp(0.0, 50000.0) / 1e5,
2354 );
2355 let pts: Vec<(f64, f64)> = if name == "leftUpArrow" {
2356 let cx = w - hw;
2357 let cy = h - hw;
2358 vec![
2359 (0.0, cy),
2360 (hl, cy - hw),
2361 (hl, cy - t / 2.0),
2362 (cx - t / 2.0, cy - t / 2.0),
2363 (cx - t / 2.0, hl),
2364 (cx - hw, hl),
2365 (cx, 0.0),
2366 (w, hl),
2367 (cx + t / 2.0, hl),
2368 (cx + t / 2.0, cy + t / 2.0),
2369 (hl, cy + t / 2.0),
2370 (hl, h),
2371 ]
2372 } else {
2373 let cx = w - hw;
2374 vec![
2375 (0.0, h - t),
2376 (cx - t / 2.0, h - t),
2377 (cx - t / 2.0, hl),
2378 (cx - hw, hl),
2379 (cx, 0.0),
2380 (w, hl),
2381 (cx + t / 2.0, hl),
2382 (cx + t / 2.0, h),
2383 (0.0, h),
2384 ]
2385 };
2386 simple(b.poly(&pts).take(), full, w, h)
2387 }
2388 "bentArrow" => {
2389 let (t, hw, hl, r) = (
2390 ss * a(0).clamp(0.0, 50000.0) / 1e5,
2391 ss * a(1).clamp(0.0, 50000.0) / 1e5,
2392 ss * a(2).clamp(0.0, 50000.0) / 1e5,
2393 ss * a(3).clamp(0.0, 100000.0) / 1e5,
2394 );
2395 let cy = hw; let ro = r.max(t);
2397 let ri = (ro - t).max(0.0);
2398 b.m(0.0, h)
2399 .l(0.0, cy - t / 2.0 + ro)
2400 .arc(ro, ro, 180.0, 90.0)
2401 .l(w - hl, cy - t / 2.0)
2402 .l(w - hl, 0.0)
2403 .l(w, cy)
2404 .l(w - hl, 2.0 * hw)
2405 .l(w - hl, cy + t / 2.0);
2406 b.l(t + ri, cy + t / 2.0);
2407 if ri > 0.0 {
2408 b.arc(ri, ri, 270.0, -90.0);
2409 }
2410 b.l(t, h).z();
2411 simple(b.take(), full, w, h)
2412 }
2413 "uturnArrow" => {
2414 let (t, hw, hl, r) = (
2415 ss * a(0).clamp(0.0, 25000.0) / 1e5,
2416 ss * a(1).clamp(0.0, 25000.0) / 1e5,
2417 ss * a(2).clamp(0.0, 25000.0) / 1e5,
2418 ss * a(3).clamp(0.0, 100000.0) / 1e5,
2419 );
2420 let hy = (h * a(4).clamp(0.0, 100000.0) / 1e5).max(hl + t);
2421 let ax = w - hw; let ro = r.max(t).min((ax + t / 2.0) / 2.0);
2423 let ri = (ro - t).max(0.0);
2424 b.m(0.0, h)
2425 .l(0.0, ro)
2426 .arc(ro, ro, 180.0, 90.0)
2427 .l(ax + t / 2.0 - ro, 0.0)
2428 .arc(ro, ro, 270.0, 90.0);
2429 b.l(ax + t / 2.0, hy - hl)
2430 .l(w, hy - hl)
2431 .l(ax, hy)
2432 .l(ax - hw, hy - hl)
2433 .l(ax - t / 2.0, hy - hl)
2434 .l(ax - t / 2.0, ro);
2435 if ri > 0.0 {
2436 b.arc(ri, ri, 0.0, -90.0);
2437 }
2438 b.l(t + ri, t);
2439 if ri > 0.0 {
2440 b.arc(ri, ri, 270.0, -90.0);
2441 }
2442 b.l(t, h).z();
2443 simple(b.take(), full, w, h)
2444 }
2445 "rightArrowCallout" | "leftArrowCallout" | "upArrowCallout" | "downArrowCallout" => {
2446 let d = match name {
2447 "rightArrowCallout" => Dir::Right,
2448 "leftArrowCallout" => Dir::Left,
2449 "upArrowCallout" => Dir::Up,
2450 _ => Dir::Down,
2451 };
2452 let (l, t) = len_thick(d, w, h);
2453 let ss2 = l.min(t);
2454 let shaft = ss2 * a(0).clamp(0.0, 100000.0) / 1e5;
2455 let head = ss2 * a(1).clamp(0.0, 100000.0) / 1e5;
2456 let hl = ss2 * a(2).clamp(0.0, 100000.0) / 1e5;
2457 let boxl = (l * a(3).clamp(0.0, 100000.0) / 1e5).min(l - hl);
2458 let c = t / 2.0;
2459 let pts = [
2460 (0.0, 0.0),
2461 (boxl, 0.0),
2462 (boxl, c - shaft / 2.0),
2463 (l - hl, c - shaft / 2.0),
2464 (l - hl, c - head),
2465 (l, c),
2466 (l - hl, c + head),
2467 (l - hl, c + shaft / 2.0),
2468 (boxl, c + shaft / 2.0),
2469 (boxl, t),
2470 (0.0, t),
2471 ];
2472 let pts = orient(d, w, h, &pts);
2473 let tr = orient(d, w, h, &[(0.0, 0.0), (boxl, t)]);
2474 simple(
2475 b.poly(&pts).take(),
2476 Rect::from_points(Point::new(tr[0].0, tr[0].1), Point::new(tr[1].0, tr[1].1)).abs(),
2477 w,
2478 h,
2479 )
2480 }
2481 "mathPlus" | "mathMinus" | "mathEqual" | "mathDivide" | "mathMultiply" | "mathNotEqual" => {
2482 math(name, w, h, a(0), a(1), a(2))
2483 }
2484 "flowChartPredefinedProcess" => {
2485 let r = Pb::new()
2486 .poly(&[(0.0, 0.0), (w, 0.0), (w, h), (0.0, h)])
2487 .take();
2488 let mut l = Pb::new();
2489 l.m(w / 8.0, 0.0)
2490 .l(w / 8.0, h)
2491 .m(w * 7.0 / 8.0, 0.0)
2492 .l(w * 7.0 / 8.0, h);
2493 Geometry {
2494 paths: vec![fill(r), stroke_only(l.take())],
2495 text_rect: Rect::new(w / 8.0, 0.0, w * 7.0 / 8.0, h),
2496 handles: vec![],
2497 sites: mid_sites(w, h),
2498 }
2499 }
2500 "flowChartInternalStorage" => {
2501 let r = Pb::new()
2502 .poly(&[(0.0, 0.0), (w, 0.0), (w, h), (0.0, h)])
2503 .take();
2504 let mut l = Pb::new();
2505 l.m(w / 8.0, 0.0)
2506 .l(w / 8.0, h)
2507 .m(0.0, h / 8.0)
2508 .l(w, h / 8.0);
2509 Geometry {
2510 paths: vec![fill(r), stroke_only(l.take())],
2511 text_rect: Rect::new(w / 8.0, h / 8.0, w, h),
2512 handles: vec![],
2513 sites: mid_sites(w, h),
2514 }
2515 }
2516 "flowChartDocument" => {
2517 b.m(0.0, 0.0)
2518 .l(w, 0.0)
2519 .l(w, h * 0.8)
2520 .c(w * 0.7, h * 0.62, w * 0.35, h * 1.08, 0.0, h * 0.88)
2521 .z();
2522 simple(b.take(), Rect::new(0.0, 0.0, w, h * 0.78), w, h)
2523 }
2524 "flowChartMultidocument" => {
2525 let (dx, dy) = (w * 0.1, h * 0.1);
2526 let mut back = Pb::new();
2527 back.poly(&[
2528 (2.0 * dx, 0.0),
2529 (w, 0.0),
2530 (w, h * 0.7),
2531 (w - dx * 0.5, h * 0.7),
2532 (w - dx * 0.5, dy * 0.5),
2533 (2.0 * dx, dy * 0.5),
2534 ]);
2535 back.poly(&[
2536 (dx, dy * 0.5),
2537 (w - dx * 0.5, dy * 0.5),
2538 (w - dx * 0.5, h * 0.78),
2539 (w - dx, h * 0.78),
2540 (w - dx, dy),
2541 (dx, dy),
2542 ]);
2543 let front = Pb::new()
2544 .m(0.0, dy)
2545 .l(w - dx, dy)
2546 .l(w - dx, h * 0.84)
2547 .c(w * 0.62, h * 0.7, w * 0.32, h * 1.08, 0.0, h * 0.9)
2548 .z()
2549 .take();
2550 Geometry {
2551 paths: vec![fill(back.take()), fill(front)],
2552 text_rect: Rect::new(0.0, dy, w - dx, h * 0.8),
2553 handles: vec![],
2554 sites: mid_sites(w, h),
2555 }
2556 }
2557 "flowChartTerminator" => {
2558 let rx = (w * 0.16).min(w / 2.0);
2559 b.m(rx, 0.0)
2560 .l(w - rx, 0.0)
2561 .arc(rx, h / 2.0, 270.0, 180.0)
2562 .l(rx, h)
2563 .arc(rx, h / 2.0, 90.0, 180.0)
2564 .z();
2565 simple(
2566 b.take(),
2567 Rect::new(rx * 0.3, h * 0.15, w - rx * 0.3, h * 0.85),
2568 w,
2569 h,
2570 )
2571 }
2572 "flowChartManualInput" => simple(
2573 b.poly(&[(0.0, h / 5.0), (w, 0.0), (w, h), (0.0, h)]).take(),
2574 Rect::new(0.0, h / 5.0, w, h),
2575 w,
2576 h,
2577 ),
2578 "flowChartOffpageConnector" => simple(
2579 b.poly(&[
2580 (0.0, 0.0),
2581 (w, 0.0),
2582 (w, h * 0.8),
2583 (w / 2.0, h),
2584 (0.0, h * 0.8),
2585 ])
2586 .take(),
2587 Rect::new(0.0, 0.0, w, h * 0.8),
2588 w,
2589 h,
2590 ),
2591 "flowChartPunchedCard" => simple(
2592 b.poly(&[(w * 0.2, 0.0), (w, 0.0), (w, h), (0.0, h), (0.0, h * 0.2)])
2593 .take(),
2594 Rect::new(0.0, h * 0.2, w, h),
2595 w,
2596 h,
2597 ),
2598 "flowChartPunchedTape" => {
2599 let a1 = h * 0.1;
2600 b.m(0.0, a1)
2601 .c(w * 0.25, a1 * 3.0, w * 0.25, -a1, w / 2.0, a1)
2602 .c(w * 0.75, a1 * 3.0, w * 0.75, -a1, w, a1)
2603 .l(w, h - a1);
2604 b.c(w * 0.75, h - 3.0 * a1, w * 0.75, h + a1, w / 2.0, h - a1)
2605 .c(w * 0.25, h - 3.0 * a1, w * 0.25, h + a1, 0.0, h - a1)
2606 .z();
2607 simple(b.take(), Rect::new(0.0, h * 0.2, w, h * 0.8), w, h)
2608 }
2609 "flowChartSummingJunction" | "flowChartOr" => {
2610 let e = Pb::new().ellipse(w / 2.0, h / 2.0, w / 2.0, h / 2.0).take();
2611 let mut l = Pb::new();
2612 if name == "flowChartOr" {
2613 l.m(w / 2.0, 0.0)
2614 .l(w / 2.0, h)
2615 .m(0.0, h / 2.0)
2616 .l(w, h / 2.0);
2617 } else {
2618 let (dx, dy) = (
2619 w / 2.0 * std::f64::consts::FRAC_1_SQRT_2,
2620 h / 2.0 * std::f64::consts::FRAC_1_SQRT_2,
2621 );
2622 l.m(w / 2.0 - dx, h / 2.0 - dy)
2623 .l(w / 2.0 + dx, h / 2.0 + dy)
2624 .m(w / 2.0 + dx, h / 2.0 - dy)
2625 .l(w / 2.0 - dx, h / 2.0 + dy);
2626 }
2627 Geometry {
2628 paths: vec![fill(e), stroke_only(l.take())],
2629 text_rect: Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355),
2630 handles: vec![],
2631 sites: ellipse_sites(w, h),
2632 }
2633 }
2634 "flowChartCollate" => simple(
2635 b.poly(&[(0.0, 0.0), (w, 0.0), (0.0, h), (w, h)]).take(),
2636 Rect::new(w / 4.0, h / 4.0, w * 0.75, h * 0.75),
2637 w,
2638 h,
2639 ),
2640 "flowChartOnlineStorage" => {
2641 let rx = w / 6.0;
2642 b.m(rx, 0.0)
2643 .l(w, 0.0)
2644 .arc(rx, h / 2.0, 270.0, -180.0)
2645 .l(rx, h)
2646 .arc(rx, h / 2.0, 90.0, 180.0)
2647 .z();
2648 simple(b.take(), Rect::new(rx, 0.0, w - rx, h), w, h)
2649 }
2650 "flowChartDelay" => {
2651 b.m(0.0, 0.0)
2652 .l(w / 2.0, 0.0)
2653 .arc(w / 2.0, h / 2.0, 270.0, 180.0)
2654 .l(0.0, h)
2655 .z();
2656 simple(b.take(), Rect::new(0.0, h * 0.15, w * 0.85, h * 0.85), w, h)
2657 }
2658 "flowChartMagneticTape" => {
2659 b.m(w / 2.0, h)
2660 .arc(w / 2.0, h / 2.0, 90.0, 90.0)
2661 .arc(w / 2.0, h / 2.0, 180.0, 90.0)
2662 .arc(w / 2.0, h / 2.0, 270.0, 90.0)
2663 .arc(w / 2.0, h / 2.0, 0.0, 45.0);
2664 let p = b.cur;
2665 b.l(w, p.y).l(w, h).z();
2666 simple(
2667 b.take(),
2668 Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355),
2669 w,
2670 h,
2671 )
2672 }
2673 "flowChartDisplay" => {
2674 let rx = w / 6.0;
2675 b.m(0.0, h / 2.0)
2676 .l(rx, 0.0)
2677 .l(w - rx, 0.0)
2678 .arc(rx, h / 2.0, 270.0, 180.0)
2679 .l(rx, h)
2680 .z();
2681 simple(b.take(), Rect::new(rx, 0.0, w - rx, h), w, h)
2682 }
2683 "star4" | "star5" | "star6" | "star7" | "star8" | "star10" | "star12" | "star16"
2684 | "star24" | "star32" => {
2685 let n: usize = name.trim_start_matches("star").parse().unwrap_or(5);
2686 let v = a(0).clamp(0.0, 50000.0);
2687 let inner = v / 50000.0;
2688 let normalized = matches!(n, 5 | 7);
2689 let mut g = simple(
2690 b.poly(&star(n, w, h, inner, normalized)).take(),
2691 full.inset(-ss * (0.5 - inner * 0.5) * 0.5),
2692 w,
2693 h,
2694 );
2695 g.handles.push(Handle::new(
2696 0,
2697 Point::new(w / 2.0, h / 2.0),
2698 Vec2::new(0.0, -h / 2.0 / 50000.0),
2699 v,
2700 0.0,
2701 50000.0,
2702 ));
2703 g
2704 }
2705 "irregularSeal1" | "irregularSeal2" => {
2706 let pts: &[(f64, f64)] = if name == "irregularSeal1" {
2707 &[
2708 (0.48, 0.18),
2709 (0.62, 0.0),
2710 (0.64, 0.2),
2711 (0.86, 0.1),
2712 (0.78, 0.3),
2713 (1.0, 0.36),
2714 (0.82, 0.5),
2715 (0.94, 0.72),
2716 (0.7, 0.66),
2717 (0.68, 0.9),
2718 (0.54, 0.72),
2719 (0.42, 1.0),
2720 (0.36, 0.74),
2721 (0.14, 0.86),
2722 (0.22, 0.62),
2723 (0.0, 0.6),
2724 (0.18, 0.44),
2725 (0.04, 0.24),
2726 (0.3, 0.3),
2727 (0.26, 0.06),
2728 ]
2729 } else {
2730 &[
2731 (0.5, 0.16),
2732 (0.58, 0.0),
2733 (0.63, 0.18),
2734 (0.78, 0.06),
2735 (0.76, 0.24),
2736 (0.96, 0.2),
2737 (0.86, 0.38),
2738 (1.0, 0.5),
2739 (0.84, 0.58),
2740 (0.92, 0.76),
2741 (0.74, 0.72),
2742 (0.76, 0.94),
2743 (0.6, 0.8),
2744 (0.5, 1.0),
2745 (0.44, 0.8),
2746 (0.28, 0.94),
2747 (0.28, 0.74),
2748 (0.08, 0.82),
2749 (0.16, 0.62),
2750 (0.0, 0.54),
2751 (0.14, 0.44),
2752 (0.02, 0.28),
2753 (0.22, 0.3),
2754 (0.2, 0.1),
2755 (0.36, 0.22),
2756 (0.4, 0.04),
2757 ]
2758 };
2759 let pts: Vec<(f64, f64)> = pts.iter().map(|(x, y)| (x * w, y * h)).collect();
2760 simple(
2761 b.poly(&pts).take(),
2762 Rect::new(w * 0.25, h * 0.3, w * 0.75, h * 0.7),
2763 w,
2764 h,
2765 )
2766 }
2767 "ribbon" | "ribbon2" => ribbon(name == "ribbon2", w, h, a(0), a(1)),
2768 "verticalScroll" | "horizontalScroll" => scroll(name == "verticalScroll", w, h, a(0)),
2769 "wave" | "doubleWave" => {
2770 let amp = h * a(0).clamp(0.0, 20000.0) / 1e5;
2771 let shift = w * a(1).clamp(-10000.0, 10000.0) / 1e5;
2772 if name == "wave" {
2773 b.m(shift.max(0.0), amp).c(
2774 w / 3.0,
2775 -amp,
2776 2.0 * w / 3.0,
2777 3.0 * amp,
2778 w + shift.min(0.0),
2779 amp,
2780 );
2781 b.l(w - shift.max(0.0), h - amp)
2782 .c(
2783 2.0 * w / 3.0,
2784 h - 3.0 * amp,
2785 w / 3.0,
2786 h + amp,
2787 -shift.min(0.0),
2788 h - amp,
2789 )
2790 .z();
2791 } else {
2792 b.m(0.0, amp)
2793 .c(w / 6.0, -amp, w / 3.0, 3.0 * amp, w / 2.0, amp)
2794 .c(2.0 * w / 3.0, -amp, 5.0 * w / 6.0, 3.0 * amp, w, amp);
2795 b.l(w, h - amp)
2796 .c(
2797 5.0 * w / 6.0,
2798 h - 3.0 * amp,
2799 2.0 * w / 3.0,
2800 h + amp,
2801 w / 2.0,
2802 h - amp,
2803 )
2804 .c(w / 3.0, h - 3.0 * amp, w / 6.0, h + amp, 0.0, h - amp)
2805 .z();
2806 }
2807 simple(b.take(), Rect::new(0.0, amp * 2.0, w, h - amp * 2.0), w, h)
2808 }
2809 "wedgeRectCallout" | "wedgeRoundRectCallout" => {
2810 let r = if name == "wedgeRoundRectCallout" {
2811 ss * a(2).clamp(0.0, 50000.0) / 1e5
2812 } else {
2813 0.0
2814 };
2815 wedge_rect(w, h, a(0), a(1), r)
2816 }
2817 "wedgeEllipseCallout" => {
2818 let tip = Point::new(w / 2.0 + w * a(0) / 1e5, h / 2.0 + h * a(1) / 1e5);
2819 let ang = (tip.y - h / 2.0).atan2(tip.x - w / 2.0).to_degrees();
2820 let s = vis(ang + 10.0, w / 2.0, h / 2.0);
2821 let p0 = Point::new(
2822 w / 2.0 + w / 2.0 * s.to_radians().cos(),
2823 h / 2.0 + h / 2.0 * s.to_radians().sin(),
2824 );
2825 let e = vis(ang - 10.0, w / 2.0, h / 2.0);
2826 let mut sw = e - s;
2827 if sw <= 0.0 {
2828 sw += 360.0;
2829 }
2830 b.m(p0.x, p0.y)
2831 .arc(w / 2.0, h / 2.0, s, sw)
2832 .l(tip.x, tip.y)
2833 .z();
2834 let mut g = simple(
2835 b.take(),
2836 Rect::new(w * 0.14645, h * 0.14645, w * 0.85355, h * 0.85355),
2837 w,
2838 h,
2839 );
2840 g.handles.push(Handle::new(
2841 0,
2842 Point::new(w / 2.0, h / 2.0),
2843 Vec2::new(w / 1e5, 0.0),
2844 a(0),
2845 -1e6,
2846 1e6,
2847 ));
2848 g.handles.push(Handle::new(
2849 1,
2850 Point::new(w / 2.0, h / 2.0),
2851 Vec2::new(0.0, h / 1e5),
2852 a(1),
2853 -1e6,
2854 1e6,
2855 ));
2856 g.handles.retain(|_| false);
2857 g.handles.push(tip_handle(w, h, tip));
2858 g
2859 }
2860 "borderCallout1" => {
2861 let r = Pb::new()
2862 .poly(&[(0.0, 0.0), (w, 0.0), (w, h), (0.0, h)])
2863 .take();
2864 let p1 = (w * a(1) / 1e5, h * a(0) / 1e5);
2865 let p2 = (w * a(3) / 1e5, h * a(2) / 1e5);
2866 let l = Pb::new().m(p1.0, p1.1).l(p2.0, p2.1).take();
2867 Geometry {
2868 paths: vec![fill(r), stroke_only(l)],
2869 text_rect: full,
2870 handles: vec![],
2871 sites: mid_sites(w, h),
2872 }
2873 }
2874 "line" | "straightConnector1" => {
2875 let l = Pb::new().m(0.0, 0.0).l(w, h).take();
2876 Geometry {
2877 paths: vec![stroke_only(l)],
2878 text_rect: full,
2879 handles: vec![],
2880 sites: vec![Point::ZERO, Point::new(w, h)],
2881 }
2882 }
2883 "bentConnector2" => {
2884 let l = Pb::new().m(0.0, 0.0).l(w, 0.0).l(w, h).take();
2885 Geometry {
2886 paths: vec![stroke_only(l)],
2887 text_rect: full,
2888 handles: vec![],
2889 sites: vec![Point::ZERO, Point::new(w, h)],
2890 }
2891 }
2892 "bentConnector3" | "bentConnector4" | "bentConnector5" => {
2893 let x = w * a(0) / 1e5;
2894 let l = Pb::new().m(0.0, 0.0).l(x, 0.0).l(x, h).l(w, h).take();
2895 let mut g = Geometry {
2896 paths: vec![stroke_only(l)],
2897 text_rect: full,
2898 handles: vec![],
2899 sites: vec![Point::ZERO, Point::new(w, h)],
2900 };
2901 g.handles.push(Handle::new(
2902 0,
2903 Point::new(0.0, h / 2.0),
2904 Vec2::new(w / 1e5, 0.0),
2905 a(0),
2906 -1e6,
2907 1e6,
2908 ));
2909 g
2910 }
2911 "curvedConnector2" | "curvedConnector3" | "curvedConnector4" | "curvedConnector5" => {
2912 let x = w * if name == "curvedConnector2" {
2913 100000.0
2914 } else {
2915 a(0)
2916 } / 1e5;
2917 let l = Pb::new()
2918 .m(0.0, 0.0)
2919 .c(x / 2.0, 0.0, x, h / 4.0, x, h / 2.0)
2920 .c(x, h * 0.75, (x + w) / 2.0, h, w, h)
2921 .take();
2922 Geometry {
2923 paths: vec![stroke_only(l)],
2924 text_rect: full,
2925 handles: vec![],
2926 sites: vec![Point::ZERO, Point::new(w, h)],
2927 }
2928 }
2929 _ => return extra::build(name, w, h, &a),
2930 };
2931 Some(g)
2932}
2933
2934fn ellipse_sites(w: f64, h: f64) -> Vec<Point> {
2935 (0..8)
2936 .map(|i| {
2937 let a = (-90.0 - 45.0 * i as f64).to_radians();
2938 Point::new(w / 2.0 + w / 2.0 * a.cos(), h / 2.0 + h / 2.0 * a.sin())
2939 })
2940 .collect()
2941}
2942
2943fn tip_handle(w: f64, h: f64, tip: Point) -> Handle {
2944 Handle {
2946 index: 0,
2947 pos: tip,
2948 origin: Point::new(w / 2.0, h / 2.0),
2949 dir: Vec2::new(w / 1e5, h / 1e5),
2950 min: -1e6,
2951 max: 1e6,
2952 }
2953}
2954
2955fn wedge_rect(w: f64, h: f64, a0: f64, a1: f64, r: f64) -> Geometry {
2956 let tip = Point::new(w / 2.0 + w * a0 / 1e5, h / 2.0 + h * a1 / 1e5);
2957 let inside = tip.x > 0.0 && tip.x < w && tip.y > 0.0 && tip.y < h;
2958 let (dx, dy) = (
2959 (tip.x - w / 2.0) / w.max(1e-9),
2960 (tip.y - h / 2.0) / h.max(1e-9),
2961 );
2962 let mut b = Pb::new();
2963 let r = r.min(w / 2.0).min(h / 2.0);
2964 let horiz = dx.abs() > dy.abs();
2966 let (bx0, bx1) = (w * 5.0 / 12.0, w * 7.0 / 12.0);
2967 let (by0, by1) = (h * 5.0 / 12.0, h * 7.0 / 12.0);
2968 let (bx0, bx1) = if tip.x < w / 2.0 {
2969 (w * 2.0 / 12.0, w * 5.0 / 12.0)
2970 } else {
2971 (bx0 + w / 12.0 * 2.0 - w / 12.0 * 2.0, bx1 + w * 2.0 / 12.0)
2972 };
2973 let (by0, by1) = if tip.y < h / 2.0 {
2974 (h * 2.0 / 12.0, h * 5.0 / 12.0)
2975 } else {
2976 (by0, by1 + h * 2.0 / 12.0)
2977 };
2978 b.m(r, 0.0);
2979 if !inside && !horiz && dy < 0.0 {
2980 b.l(bx0, 0.0).l(tip.x, tip.y).l(bx1, 0.0);
2981 }
2982 b.l(w - r, 0.0);
2983 if r > 0.0 {
2984 b.arc(r, r, 270.0, 90.0);
2985 }
2986 if !inside && horiz && dx > 0.0 {
2987 b.l(w, by0).l(tip.x, tip.y).l(w, by1);
2988 }
2989 b.l(w, h - r);
2990 if r > 0.0 {
2991 b.arc(r, r, 0.0, 90.0);
2992 }
2993 if !inside && !horiz && dy > 0.0 {
2994 b.l(bx1, h).l(tip.x, tip.y).l(bx0, h);
2995 }
2996 b.l(r, h);
2997 if r > 0.0 {
2998 b.arc(r, r, 90.0, 90.0);
2999 }
3000 if !inside && horiz && dx < 0.0 {
3001 b.l(0.0, by1).l(tip.x, tip.y).l(0.0, by0);
3002 }
3003 b.l(0.0, r);
3004 if r > 0.0 {
3005 b.arc(r, r, 180.0, 90.0);
3006 }
3007 b.z();
3008 let mut g = simple(
3009 b.take(),
3010 Rect::new(0.0, 0.0, w, h).inset(-r * 0.29289),
3011 w,
3012 h,
3013 );
3014 g.handles.push(tip_handle(w, h, tip));
3015 g
3016}
3017
3018fn cloud(name: &str, w: f64, h: f64, a0: f64, a1: f64) -> Geometry {
3019 let n = 10;
3020 let pts: Vec<Point> = (0..n)
3021 .map(|i| {
3022 let t = (i as f64 / n as f64 * 360.0 - 90.0).to_radians();
3023 let wob = if i % 2 == 0 { 1.0 } else { 0.92 };
3024 Point::new(0.5 + 0.40 * wob * t.cos(), 0.5 + 0.36 * wob * t.sin())
3025 })
3026 .collect();
3027 let mut b = Pb::new();
3028 let first = pts.first().copied().unwrap_or(Point::ZERO);
3029 b.m(first.x, first.y);
3030 for i in 0..n {
3031 let p = pts.get(i).copied().unwrap_or(Point::ZERO);
3032 let q = pts.get((i + 1) % n).copied().unwrap_or(Point::ZERO);
3033 let mid = p.midpoint(q);
3034 let out = (mid - Point::new(0.5, 0.5)).normalize();
3035 let bulge = (q - p).hypot() * 0.75;
3036 let c1 = p + (mid - p) * 0.2 + out * bulge;
3037 let c2 = q + (mid - q) * 0.2 + out * bulge;
3038 b.c(c1.x, c1.y, c2.x, c2.y, q.x, q.y);
3039 }
3040 b.z();
3041 let mut path = b.take();
3042 let bb = path.bounding_box();
3043 let sx = if bb.width() > 0.0 {
3044 w / bb.width()
3045 } else {
3046 1.0
3047 };
3048 let sy = if bb.height() > 0.0 {
3049 h / bb.height()
3050 } else {
3051 1.0
3052 };
3053 path.apply_affine(
3054 kurbo::Affine::scale_non_uniform(sx, sy) * kurbo::Affine::translate(-bb.origin().to_vec2()),
3055 );
3056 let mut paths = vec![fill(path)];
3057 let text_rect = Rect::new(w * 0.2, h * 0.22, w * 0.8, h * 0.78);
3058 let mut handles = vec![];
3059 if name == "cloudCallout" {
3060 let tip = Point::new(w / 2.0 + w * a0 / 1e5, h / 2.0 + h * a1 / 1e5);
3061 let c = Point::new(w / 2.0, h / 2.0);
3062 let mut bubbles = Pb::new();
3063 for (k, r) in [(0.55, 0.06), (0.75, 0.045), (0.95, 0.03)] {
3064 let p = c + (tip - c) * k;
3065 if !(p.x > w * 0.1 && p.x < w * 0.9 && p.y > h * 0.1 && p.y < h * 0.9) || k > 0.9 {
3066 bubbles.ellipse(p.x, p.y, w * r, h * r);
3067 }
3068 }
3069 paths.push(fill(bubbles.take()));
3070 handles.push(tip_handle(w, h, tip));
3071 }
3072 Geometry {
3073 paths,
3074 text_rect,
3075 handles,
3076 sites: mid_sites(w, h),
3077 }
3078}
3079
3080fn math(name: &str, w: f64, h: f64, a0: f64, a1: f64, a2: f64) -> Geometry {
3081 let ss = w.min(h);
3082 let t = ss * a0.clamp(0.0, 50000.0) / 1e5;
3083 let (cx, cy) = (w / 2.0, h / 2.0);
3084 let mx = w * 0.0735;
3085 let my = h * 0.0735;
3086 let mut b = Pb::new();
3087 match name {
3088 "mathPlus" => {
3089 let (hx, hy) = (cx - mx, cy - my);
3090 b.poly(&[
3091 (cx - t / 2.0, cy - hy),
3092 (cx + t / 2.0, cy - hy),
3093 (cx + t / 2.0, cy - t / 2.0),
3094 (cx + hx, cy - t / 2.0),
3095 (cx + hx, cy + t / 2.0),
3096 (cx + t / 2.0, cy + t / 2.0),
3097 (cx + t / 2.0, cy + hy),
3098 (cx - t / 2.0, cy + hy),
3099 (cx - t / 2.0, cy + t / 2.0),
3100 (cx - hx, cy + t / 2.0),
3101 (cx - hx, cy - t / 2.0),
3102 (cx - t / 2.0, cy - t / 2.0),
3103 ]);
3104 }
3105 "mathMinus" => {
3106 b.poly(&[
3107 (mx, cy - t / 2.0),
3108 (w - mx, cy - t / 2.0),
3109 (w - mx, cy + t / 2.0),
3110 (mx, cy + t / 2.0),
3111 ]);
3112 }
3113 "mathEqual" => {
3114 let gap = h * a1.clamp(0.0, 100000.0) / 1e5;
3115 b.poly(&[
3116 (mx, cy - gap / 2.0 - t),
3117 (w - mx, cy - gap / 2.0 - t),
3118 (w - mx, cy - gap / 2.0),
3119 (mx, cy - gap / 2.0),
3120 ]);
3121 b.poly(&[
3122 (mx, cy + gap / 2.0),
3123 (w - mx, cy + gap / 2.0),
3124 (w - mx, cy + gap / 2.0 + t),
3125 (mx, cy + gap / 2.0 + t),
3126 ]);
3127 }
3128 "mathNotEqual" => {
3129 let gap = h * a2.clamp(0.0, 100000.0) / 1e5;
3130 b.poly(&[
3131 (mx, cy - gap / 2.0 - t),
3132 (w - mx, cy - gap / 2.0 - t),
3133 (w - mx, cy - gap / 2.0),
3134 (mx, cy - gap / 2.0),
3135 ]);
3136 b.poly(&[
3137 (mx, cy + gap / 2.0),
3138 (w - mx, cy + gap / 2.0),
3139 (w - mx, cy + gap / 2.0 + t),
3140 (mx, cy + gap / 2.0 + t),
3141 ]);
3142 let ang = (a1 / 60000.0).to_radians();
3143 let dir = Vec2::new(ang.cos(), ang.sin());
3144 let len = (h / 2.0 - my) / ang.sin().abs().max(0.2);
3145 let perp = Vec2::new(-dir.y, dir.x) * (t / 2.0);
3146 let c = Point::new(cx, cy);
3147 let p = [
3148 c - dir * len - perp,
3149 c + dir * len - perp,
3150 c + dir * len + perp,
3151 c - dir * len + perp,
3152 ];
3153 b.poly(&p.map(|q| (q.x, q.y)));
3154 }
3155 "mathDivide" => {
3156 let gap = h * a1.clamp(0.0, 100000.0) / 1e5;
3157 let r = ss * a2.clamp(0.0, 50000.0) / 1e5 / 2.0;
3158 b.poly(&[
3159 (mx, cy - t / 2.0),
3160 (w - mx, cy - t / 2.0),
3161 (w - mx, cy + t / 2.0),
3162 (mx, cy + t / 2.0),
3163 ]);
3164 b.ellipse(cx, cy - t / 2.0 - gap - r, r, r);
3165 b.ellipse(cx, cy + t / 2.0 + gap + r, r, r);
3166 }
3167 _ => {
3168 let len = ((cx - mx).powi(2) + (cy - my).powi(2)).sqrt();
3170 let c = Point::new(cx, cy);
3171 for d in [Vec2::new(cx - mx, cy - my), Vec2::new(cx - mx, -(cy - my))] {
3172 let dir = d / d.hypot().max(1e-9);
3173 let perp = Vec2::new(-dir.y, dir.x) * (t / 2.0);
3174 let p = [
3175 c - dir * len - perp,
3176 c + dir * len - perp,
3177 c + dir * len + perp,
3178 c - dir * len + perp,
3179 ];
3180 b.poly(&p.map(|q| (q.x, q.y)));
3181 }
3182 }
3183 }
3184 Geometry {
3185 paths: vec![fill(b.take())],
3186 text_rect: Rect::new(mx, cy - t / 2.0, w - mx, cy + t / 2.0),
3187 handles: vec![],
3188 sites: mid_sites(w, h),
3189 }
3190}
3191
3192fn ribbon(up: bool, w: f64, h: f64, a0: f64, a1: f64) -> Geometry {
3193 let fold = h * a0.clamp(0.0, 33333.0) / 1e5;
3194 let cw = w * a1.clamp(25000.0, 75000.0) / 1e5; let x0 = (w - cw) / 2.0;
3196 let x1 = w - x0;
3197 let ear = w / 8.0;
3198 let (top, bot) = (0.0, h);
3199 let mut b = Pb::new();
3200 let mut shade_p = Pb::new();
3201 if up {
3202 b.poly(&[(x0, top), (x1, top), (x1, bot - fold), (x0, bot - fold)]);
3204 let mut ends = Pb::new();
3205 ends.poly(&[
3206 (0.0, fold),
3207 (x0 + ear * 0.5, fold),
3208 (x0 + ear * 0.5, bot),
3209 (0.0, bot),
3210 (ear, (fold + bot) / 2.0),
3211 ]);
3212 ends.poly(&[
3213 (w, fold),
3214 (x1 - ear * 0.5, fold),
3215 (x1 - ear * 0.5, bot),
3216 (w, bot),
3217 (w - ear, (fold + bot) / 2.0),
3218 ]);
3219 shade_p.poly(&[
3220 (x0, bot - fold),
3221 (x0 + ear * 0.5, bot - fold),
3222 (x0 + ear * 0.5, bot),
3223 ]);
3224 shade_p.poly(&[
3225 (x1, bot - fold),
3226 (x1 - ear * 0.5, bot - fold),
3227 (x1 - ear * 0.5, bot),
3228 ]);
3229 let text = Rect::new(x0, top, x1, bot - fold);
3230 return Geometry {
3231 paths: vec![
3232 fill(ends.take()),
3233 shade(shade_p.take(), FillMode::DarkenLess),
3234 fill(b.take()),
3235 ],
3236 text_rect: text,
3237 handles: vec![],
3238 sites: mid_sites(w, h),
3239 };
3240 }
3241 b.poly(&[(x0, top + fold), (x1, top + fold), (x1, bot), (x0, bot)]);
3242 let mut ends = Pb::new();
3243 ends.poly(&[
3244 (0.0, top),
3245 (x0 + ear * 0.5, top),
3246 (x0 + ear * 0.5, bot - fold),
3247 (0.0, bot - fold),
3248 (ear, (bot - fold) / 2.0),
3249 ]);
3250 ends.poly(&[
3251 (w, top),
3252 (x1 - ear * 0.5, top),
3253 (x1 - ear * 0.5, bot - fold),
3254 (w, bot - fold),
3255 (w - ear, (bot - fold) / 2.0),
3256 ]);
3257 shade_p.poly(&[
3258 (x0, top + fold),
3259 (x0 + ear * 0.5, top + fold),
3260 (x0 + ear * 0.5, top),
3261 ]);
3262 shade_p.poly(&[
3263 (x1, top + fold),
3264 (x1 - ear * 0.5, top + fold),
3265 (x1 - ear * 0.5, top),
3266 ]);
3267 let text = Rect::new(x0, top + fold, x1, bot);
3268 Geometry {
3269 paths: vec![
3270 fill(ends.take()),
3271 shade(shade_p.take(), FillMode::DarkenLess),
3272 fill(b.take()),
3273 ],
3274 text_rect: text,
3275 handles: vec![],
3276 sites: mid_sites(w, h),
3277 }
3278}
3279
3280fn scroll(vertical: bool, w: f64, h: f64, a0: f64) -> Geometry {
3281 let ss = w.min(h);
3282 let r = ss * a0.clamp(0.0, 25000.0) / 1e5 / 2.0;
3283 let mut b = Pb::new();
3284 let mut curl = Pb::new();
3285 if vertical {
3286 b.poly(&[(r, r * 2.0), (w - r, r * 2.0), (w - r, h - r), (r, h - r)]);
3287 b.ellipse(w / 2.0, r, w / 2.0 - r * 0.0, r);
3288 curl.ellipse(r, h - r, r, r);
3289 curl.ellipse(w - r * 2.0, r, r * 0.5, r * 0.5);
3290 let text = Rect::new(r, r * 2.0, w - r, h - r);
3291 return Geometry {
3292 paths: vec![fill(b.take()), shade(curl.take(), FillMode::DarkenLess)],
3293 text_rect: text,
3294 handles: vec![],
3295 sites: mid_sites(w, h),
3296 };
3297 }
3298 b.poly(&[(r * 2.0, r), (w - r, r), (w - r, h - r), (r * 2.0, h - r)]);
3299 b.ellipse(r, h / 2.0, r, h / 2.0);
3300 curl.ellipse(w - r, r, r, r);
3301 curl.ellipse(r, h - r * 2.0, r * 0.5, r * 0.5);
3302 let text = Rect::new(r * 2.0, r, w - r, h - r);
3303 Geometry {
3304 paths: vec![fill(b.take()), shade(curl.take(), FillMode::DarkenLess)],
3305 text_rect: text,
3306 handles: vec![],
3307 sites: mid_sites(w, h),
3308 }
3309}
3310
3311fn action_button(name: &str, w: f64, h: f64) -> Geometry {
3312 let body = corner_rect(w, h, [Corner::Square; 4]);
3313 let ss = w.min(h);
3314 let (cx, cy) = (w / 2.0, h / 2.0);
3315 let s = ss * 0.3;
3316 let mut g = Pb::new();
3317 match name {
3318 "actionButtonBackPrevious" => {
3319 g.poly(&[(cx + s, cy - s), (cx + s, cy + s), (cx - s, cy)]);
3320 }
3321 "actionButtonForwardNext" => {
3322 g.poly(&[(cx - s, cy - s), (cx - s, cy + s), (cx + s, cy)]);
3323 }
3324 "actionButtonBeginning" => {
3325 g.poly(&[(cx + s, cy - s), (cx + s, cy + s), (cx - s * 0.6, cy)]);
3326 g.poly(&[
3327 (cx - s, cy - s),
3328 (cx - s * 0.7, cy - s),
3329 (cx - s * 0.7, cy + s),
3330 (cx - s, cy + s),
3331 ]);
3332 }
3333 "actionButtonEnd" => {
3334 g.poly(&[(cx - s, cy - s), (cx - s, cy + s), (cx + s * 0.6, cy)]);
3335 g.poly(&[
3336 (cx + s * 0.7, cy - s),
3337 (cx + s, cy - s),
3338 (cx + s, cy + s),
3339 (cx + s * 0.7, cy + s),
3340 ]);
3341 }
3342 "actionButtonHome" => {
3343 g.poly(&[
3344 (cx, cy - s),
3345 (cx + s, cy),
3346 (cx + s * 0.7, cy),
3347 (cx + s * 0.7, cy + s),
3348 (cx - s * 0.7, cy + s),
3349 (cx - s * 0.7, cy),
3350 (cx - s, cy),
3351 ]);
3352 }
3353 "actionButtonInformation" | "actionButtonHelp" => {
3354 g.ellipse(cx, cy, s, s);
3355 }
3356 "actionButtonReturn" => {
3357 g.m(cx + s, cy - s * 0.6)
3358 .l(cx + s, cy + s * 0.2)
3359 .q(cx + s, cy + s * 0.8, cx + s * 0.4, cy + s * 0.8)
3360 .l(cx - s * 0.5, cy + s * 0.8);
3361 g.l(cx - s * 0.5, cy + s * 0.4)
3362 .l(cx - s, cy + s * 0.9)
3363 .l(cx - s * 0.5, cy + s * 1.4)
3364 .l(cx - s * 0.5, cy + s)
3365 .l(cx + s * 0.6, cy + s)
3366 .l(cx + s * 0.6, cy - s * 0.6)
3367 .z();
3368 }
3369 "actionButtonMovie" => {
3370 g.poly(&[
3371 (cx - s, cy - s * 0.6),
3372 (cx + s * 0.4, cy - s * 0.6),
3373 (cx + s * 0.4, cy + s * 0.6),
3374 (cx - s, cy + s * 0.6),
3375 ]);
3376 g.poly(&[
3377 (cx + s * 0.4, cy),
3378 (cx + s, cy - s * 0.5),
3379 (cx + s, cy + s * 0.5),
3380 ]);
3381 }
3382 "actionButtonDocument" => {
3383 g.poly(&[
3384 (cx - s * 0.7, cy - s),
3385 (cx + s * 0.3, cy - s),
3386 (cx + s * 0.7, cy - s * 0.6),
3387 (cx + s * 0.7, cy + s),
3388 (cx - s * 0.7, cy + s),
3389 ]);
3390 }
3391 "actionButtonSound" => {
3392 g.poly(&[
3393 (cx - s, cy - s * 0.35),
3394 (cx - s * 0.5, cy - s * 0.35),
3395 (cx, cy - s),
3396 (cx, cy + s),
3397 (cx - s * 0.5, cy + s * 0.35),
3398 (cx - s, cy + s * 0.35),
3399 ]);
3400 }
3401 _ => {}
3402 }
3403 Geometry {
3404 paths: vec![fill(body), shade(g.take(), FillMode::DarkenLess)],
3405 text_rect: Rect::new(0.0, 0.0, w, h),
3406 handles: vec![],
3407 sites: mid_sites(w, h),
3408 }
3409}
3410
3411#[cfg(test)]
3412mod tests {
3413 use super::*;
3414 use kurbo::Shape;
3415
3416 #[test]
3417 fn every_catalog_preset_builds_within_bounds() {
3418 for p in CATALOG {
3419 for (w, h) in [
3420 (200.0, 100.0),
3421 (100.0, 200.0),
3422 (50.0, 50.0),
3423 (0.0, 0.0),
3424 (1.0, 300.0),
3425 ] {
3426 let g =
3427 build(p.name, w, h, &[]).unwrap_or_else(|| panic!("{} did not build", p.name));
3428 assert!(!g.paths.is_empty(), "{} has no paths", p.name);
3429 for s in &g.paths {
3430 let bb = s.path.bounding_box();
3431 if w > 0.0
3432 && h > 0.0
3433 && !p.name.contains("Connector")
3434 && !p.name.contains("allout")
3435 {
3436 let tol = w.max(h) * 0.35 + 1.0;
3437 assert!(
3438 bb.x0 >= -tol && bb.y0 >= -tol && bb.x1 <= w + tol && bb.y1 <= h + tol,
3439 "{} {w}x{h} out of bounds: {bb:?}",
3440 p.name
3441 );
3442 }
3443 assert!(
3444 bb.x0.is_finite() && bb.y1.is_finite(),
3445 "{} produced non-finite geometry",
3446 p.name
3447 );
3448 }
3449 assert!(
3450 g.text_rect.x0.is_finite() && g.text_rect.y1.is_finite(),
3451 "{}",
3452 p.name
3453 );
3454 }
3455 }
3456 }
3457
3458 #[test]
3459 fn hostile_adjust_values_never_panic() {
3460 for p in CATALOG {
3461 for v in [f64::NAN, f64::INFINITY, -1e12, 1e12, -1.0, 0.0] {
3462 let adj = vec![v; 6];
3463 let g = build(p.name, 120.0, 80.0, &adj);
3464 assert!(g.is_some(), "{}", p.name);
3465 }
3466 }
3467 assert!(build("rect", f64::NAN, 10.0, &[]).is_none());
3468 assert!(build("noSuchShape", 10.0, 10.0, &[]).is_none());
3469 }
3470
3471 #[test]
3472 fn catalog_names_are_unique() {
3473 let mut names: Vec<&str> = CATALOG.iter().map(|p| p.name).collect();
3474 names.sort_unstable();
3475 let n = names.len();
3476 names.dedup();
3477 assert_eq!(n, names.len());
3478 assert!(n >= 140, "catalog has {n} presets");
3479 }
3480
3481 #[test]
3482 fn round_rect_handle_maps_back() {
3483 let g = build("roundRect", 200.0, 100.0, &[16667.0]).unwrap();
3484 let h = g.handles.first().unwrap();
3485 assert!((h.value_at(h.pos) - 16667.0).abs() <= 1.0);
3486 assert_eq!(h.value_at(Point::new(-50.0, 0.0)), 0.0);
3487 assert_eq!(h.value_at(Point::new(500.0, 0.0)), 50000.0);
3488 }
3489
3490 #[test]
3491 fn rect_area_and_ellipse_area() {
3492 let g = build("rect", 100.0, 50.0, &[]).unwrap();
3493 assert!((g.paths[0].path.area().abs() - 5000.0).abs() < 1e-6);
3494 let e = build("ellipse", 100.0, 50.0, &[]).unwrap();
3495 let expect = std::f64::consts::PI * 50.0 * 25.0;
3496 assert!((e.paths[0].path.area().abs() - expect).abs() / expect < 0.01);
3497 }
3498
3499 #[test]
3500 fn lines_are_open() {
3501 assert!(build("line", 10.0, 10.0, &[]).unwrap().is_open());
3502 assert!(!build("rect", 10.0, 10.0, &[]).unwrap().is_open());
3503 }
3504}