1use alloc::collections::{BTreeMap, BTreeSet};
83use alloc::rc::Rc;
84use alloc::string::String;
85use alloc::vec::Vec;
86use core::cell::RefCell;
87use core::ops::Range;
88
89use crate::canvas::{CanvasContent, CanvasDesc, CanvasGpu, CanvasView};
90use crate::cmd::{Draw, DrawRef, Gfx};
91use crate::consts::*;
92use crate::keys::*;
93use crate::layout::dp_at;
94use crate::theme::{color_mix, THEME_N};
95use crate::Ui;
96use gfx::dash::{dash_pattern, dash_stroke, DStyle, LCAP_FLAT};
97use gfx::{Surface, FMT_XRGB};
98use rt::fx::{floor_i, sqrt};
99
100pub const DG_SELECT: u32 = 0; pub const DG_PAN: u32 = 1; pub const DG_CONNECT: u32 = 2; pub const DG_PLACE: u32 = 3; pub const DR_STRAIGHT: u8 = 0;
107pub const DR_BEZIER: u8 = 1;
108pub const DR_ORTHO: u8 = 2; pub const DN_LOCKED: u32 = 1; pub const DN_NOSELECT: u32 = 2; pub const DN_FRAME: u32 = 4; pub const DN_TOGGLE: u32 = 8; pub const DN_STEP: u32 = 16; pub const DN_RESIZE: u32 = 32; pub const DN_OUTLINE: u32 = 64; pub const DN_HITMIN: u32 = 128; pub const DPT_HIDDEN: u32 = 1; pub const DPT_USED: u32 = 2; const CELL: f32 = 256.0; const CELLS_MAX: i64 = 4096; const SPAN_MAX: i64 = 64; const DRAG_DIP: i32 = 4;
126const HITMIN_DIP: i32 = 16;
127const STUB: f32 = 16.0; const EPS: f32 = 0.01;
129const MOVING_MS: u32 = 160;
131const ZOOMS: [f32; 16] = [0.1, 0.125, 0.167, 0.25, 0.333, 0.5, 0.667, 0.75, 1.0, 1.25, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0];
132const S_NONE: u8 = 0;
134const S_L: u8 = 1;
135const S_R: u8 = 2;
136const S_T: u8 = 3;
137const S_B: u8 = 4;
138
139#[derive(Clone, Copy, Debug, PartialEq)]
144pub struct Viewport {
145 pub zoom: f32,
146 pub cx: f64,
147 pub cy: f64,
148 pub rect: [i32; 4],
149 pub dpi: i32,
150}
151
152impl Default for Viewport {
153 fn default() -> Viewport {
154 Viewport { zoom: 1.0, cx: 0.0, cy: 0.0, rect: [0; 4], dpi: 96 }
155 }
156}
157
158impl Viewport {
159 pub const ZOOM_MIN: f32 = 0.1;
160 pub const ZOOM_MAX: f32 = 8.0;
161
162 pub fn scale(&self) -> f64 {
164 clampz(self.zoom) as f64 * self.dpi.max(1) as f64 / 96.0
165 }
166
167 fn mid(&self) -> (f64, f64) {
168 (self.rect[0] as f64 + self.rect[2] as f64 * 0.5, self.rect[1] as f64 + self.rect[3] as f64 * 0.5)
169 }
170
171 fn world64(&self, x: f64, y: f64) -> (f64, f64) {
172 let s = self.scale();
173 let (mx, my) = self.mid();
174 ((x - mx) / s + self.cx, (y - my) / s + self.cy)
175 }
176
177 pub fn to_screen(&self, p: [f32; 2]) -> [f32; 2] {
179 let s = self.scale();
180 let (mx, my) = self.mid();
181 [(mx + (p[0] as f64 - self.cx) * s) as f32, (my + (p[1] as f64 - self.cy) * s) as f32]
182 }
183
184 pub fn to_world(&self, x: f32, y: f32) -> [f32; 2] {
186 let (wx, wy) = self.world64(x as f64, y as f64);
187 [wx as f32, wy as f32]
188 }
189
190 pub fn world_rect(&self) -> [f32; 4] {
192 let r = self.rect;
193 let a = self.to_world(r[0] as f32, r[1] as f32);
194 let b = self.to_world((r[0] + r[2]) as f32, (r[1] + r[3]) as f32);
195 [a[0], a[1], b[0], b[1]]
196 }
197
198 pub fn rect_to_screen(&self, r: [f32; 4]) -> [f32; 4] {
200 let a = self.to_screen([r[0], r[1]]);
201 let s = self.scale() as f32;
202 [a[0], a[1], r[2] * s, r[3] * s]
203 }
204
205 pub fn zoom_at(&mut self, zoom: f32, sx: f32, sy: f32) {
207 let (wx, wy) = self.world64(sx as f64, sy as f64);
208 self.zoom = clampz(zoom);
209 let s = self.scale();
210 let (mx, my) = self.mid();
211 self.cx = wx - (sx as f64 - mx) / s;
212 self.cy = wy - (sy as f64 - my) / s;
213 }
214
215 pub fn step(&mut self, dir: i32, sx: f32, sy: f32) {
217 let z = self.zoom;
218 let n = if dir > 0 {
219 ZOOMS.iter().copied().find(|&p| p > z * 1.001)
220 } else {
221 ZOOMS.iter().rev().copied().find(|&p| p < z * 0.999)
222 };
223 if let Some(n) = n {
224 self.zoom_at(n, sx, sy);
225 }
226 }
227
228 pub fn fit(&mut self, b: [f32; 4], margin: i32) {
230 let m = dp_at(margin, self.dpi) as f64 * 2.0;
231 let (w, h) = ((b[2] - b[0]) as f64, (b[3] - b[1]) as f64);
232 let (vw, vh) = (self.rect[2] as f64 - m, self.rect[3] as f64 - m);
233 let d = self.dpi.max(1) as f64 / 96.0;
234 let mut z = 1.0f64;
235 if w > 0.0 && vw > 0.0 {
236 z = z.min(vw / (w * d));
237 }
238 if h > 0.0 && vh > 0.0 {
239 z = z.min(vh / (h * d));
240 }
241 self.zoom = clampz(z as f32);
242 self.cx = (b[0] as f64 + b[2] as f64) * 0.5;
243 self.cy = (b[1] as f64 + b[3] as f64) * 0.5;
244 }
245}
246
247fn clampz(z: f32) -> f32 {
248 if z > 0.0 { z.clamp(Viewport::ZOOM_MIN, Viewport::ZOOM_MAX) } else { 1.0 }
249}
250
251#[derive(Clone, Debug, Default)]
255pub struct DNode {
256 pub key: u64,
257 pub rect: [f32; 4],
258 pub ports: Range<u32>,
259 pub flags: u32,
260}
261
262#[derive(Clone, Copy, Debug, Default)]
264pub struct DPort {
265 pub key: u64,
266 pub at: [f32; 2],
267 pub flags: u32,
268}
269
270#[derive(Clone, Debug, Default)]
273pub struct DEdge {
274 pub key: u64,
275 pub a: u64,
276 pub b: u64,
277 pub route: u8,
278 pub wps: Range<u32>,
279}
280
281#[derive(Clone, Copy)]
283pub struct DModel<'a> {
284 pub nodes: &'a [DNode],
285 pub ports: &'a [DPort],
286 pub edges: &'a [DEdge],
287 pub wps: &'a [[f32; 2]],
288 pub ver: u32,
289}
290
291#[derive(Clone, Copy, Debug, PartialEq, Default)]
293#[non_exhaustive]
294pub enum DHit {
295 #[default]
296 None,
297 Node(u64),
298 Port(u64),
299 Edge { key: u64, seg: u32, t: f32 },
302 Waypoint(u64, u32),
304 Mid(u64, u32),
306 Toggle(u64),
308 Resize(u64),
310}
311
312impl DHit {
313 pub fn key(&self) -> u64 {
315 match *self {
316 DHit::None => 0,
317 DHit::Node(k) | DHit::Port(k) | DHit::Waypoint(k, _) | DHit::Mid(k, _) | DHit::Toggle(k) | DHit::Resize(k) => k,
318 DHit::Edge { key, .. } => key,
319 }
320 }
321}
322
323fn same(a: DHit, b: DHit) -> bool {
325 core::mem::discriminant(&a) == core::mem::discriminant(&b) && a.key() == b.key()
326}
327
328#[derive(Clone, Debug, PartialEq)]
330#[non_exhaustive]
331pub enum DEv {
332 Select,
334 Move { keys: Vec<u64>, d: [f32; 2], total: [f32; 2], done: bool },
337 Connect { from: u64, to: u64, quick: bool },
339 ConnectDrag { from: u64, at: [f32; 2], over: u64 },
341 ConnectCancel,
343 Waypoint { edge: u64, idx: u32, at: [f32; 2], insert: bool, done: bool },
346 WaypointDelete { edge: u64, idx: u32 },
348 Activate(DHit),
350 Context(DHit, [i32; 2]),
352 Delete,
354 Click { at: [f32; 2] },
356 DoubleClick { at: [f32; 2] },
359 ViewChanged,
361 Toggle(u64),
363 Resize { key: u64, rect: [f32; 4], done: bool },
366}
367
368#[derive(Clone, Debug, Default)]
373pub struct DgRoute {
374 pub key: u64,
375 pub pts: Vec<[f32; 2]>,
376 pub marks: Vec<u32>,
377 pub bb: [f32; 4],
378}
379
380#[derive(Default)]
383pub struct DgGeo {
384 pub routes: Vec<DgRoute>,
385 cells: BTreeMap<(i32, i32), (Vec<u32>, Vec<u32>)>,
388 big: (Vec<u32>, Vec<u32>),
389 counts: (usize, usize),
390}
391
392pub struct DgFrame<'a> {
394 pub vp: Viewport,
395 pub clip: [f32; 4],
397 pub geo: &'a DgGeo,
398 pub theme: &'a [u32],
400 pub bg: u32,
401 pub fonts: [(u32, i32); 7],
403 pub dark: bool,
404 pub moving: bool,
408}
409
410impl DgFrame<'_> {
411 pub fn color(&self, tok: u32) -> u32 {
413 self.theme.get(((tok & 252) / 4) as usize).copied().unwrap_or(0xFF00_0000)
414 }
415
416 pub fn visible(&self, r: [f32; 4]) -> bool {
418 let g = 2.0 / self.vp.scale() as f32;
419 overlaps([r[0] - g, r[1] - g, r[0] + r[2] + g, r[1] + r[3] + g], self.clip)
420 }
421
422 pub fn route_visible(&self, rt: &DgRoute, width: f32) -> bool {
425 let s = self.vp.scale() as f32;
426 let g = ((width * s).max(1.0) * 4.0 + 6.0) / s;
427 rt.pts.len() >= 2 && overlaps([rt.bb[0] - g, rt.bb[1] - g, rt.bb[2] + g, rt.bb[3] + g], self.clip)
428 }
429
430 pub fn find(&self, margin: f32, edges: bool) -> Vec<u32> {
434 let g = margin / (self.vp.scale() as f32).max(1e-6);
435 let c = self.clip;
436 let mut v = Vec::new();
437 self.geo.find([c[0] - g, c[1] - g, c[2] + g, c[3] + g], edges, &mut v);
438 v
439 }
440
441 pub fn font(&self, ts: u32) -> (u32, i32) {
443 let (f, s) = self.fonts[((ts & 7) as usize).min(6)];
444 (f, (s as f32 * clampz(self.vp.zoom)) as i32)
445 }
446}
447
448pub trait DgPaint {
451 fn paint(&self, g: &mut Gfx, f: &DgFrame);
452 fn paint_under(&self, _g: &mut Gfx, _f: &DgFrame) {}
454}
455
456#[derive(Clone, Default)]
459enum Gesture {
460 #[default]
461 Idle,
462 Press { hit: DHit, px: [i32; 2], at: [f32; 2], mods: u32, was: bool },
465 Hold,
467 Pan { btn: u32, px: [i32; 2], cx: f64, cy: f64 },
468 Move { keys: Vec<u64>, start: [f32; 2], anchor: [f32; 2], sent: [f32; 2], step: bool },
470 Resize { key: u64, orig: [f32; 4], start: [f32; 2], at: [f32; 4], started: bool },
472 Marquee { start: [f32; 2], cur: [f32; 2], mods: u32, base: BTreeSet<u64> },
473 Connect { from: u64, quick: bool, armed: bool, at: [f32; 2], over: u64 },
475 Waypoint { edge: u64, idx: u32, orig: [f32; 2], insert: bool, started: bool, at: [f32; 2] },
476}
477
478#[derive(Default)]
480struct Index {
481 built: bool,
482 ver: u32,
483 counts: [usize; 4],
484 nodes: BTreeMap<u64, u32>,
485 ports: BTreeMap<u64, (u32, u32)>,
486 edges: BTreeMap<u64, u32>,
487 geo: Rc<DgGeo>,
488 bounds: Option<[f32; 4]>,
489}
490
491pub struct DiagramState {
494 pub vp: Viewport,
495 pub sel: BTreeSet<u64>,
496 pub focus: u64,
498 pub grid: f32,
500 pub snap: bool,
502 pub hover: DHit,
504 pub edge_width: f32,
506 pub test_keys: u32,
508 pub tab_edges: bool,
511 pub name: String,
513 pub frame_bar: f32,
516 fit_req: bool,
517 pending_sel: bool,
518 g: Gesture,
519 ix: Index,
520 last_hit: Option<DHit>,
523 last_ms: u32,
524 last_px: [i32; 2],
525 visible: Vec<u32>,
526 paint_obj: Option<(u64, Rc<Wrap>)>,
527 mini_obj: Option<(u64, DrawRef)>,
528 mini_ext: Option<[f32; 4]>,
529 store: Rc<RefCell<Backing>>,
531 moving_until: u32,
533 pub gpu: CanvasGpu,
535 id: u32,
537}
538
539impl Default for DiagramState {
540 fn default() -> DiagramState {
541 DiagramState::new()
542 }
543}
544
545impl DiagramState {
546 pub fn new() -> DiagramState {
548 DiagramState {
549 vp: Viewport::default(),
550 sel: BTreeSet::new(),
551 focus: 0,
552 grid: 16.0,
553 snap: true,
554 hover: DHit::None,
555 edge_width: 2.0,
556 test_keys: 0,
557 tab_edges: false,
558 name: String::new(),
559 frame_bar: 24.0,
560 fit_req: true,
561 pending_sel: false,
562 g: Gesture::Idle,
563 ix: Index::default(),
564 last_hit: None,
565 last_ms: 0,
566 last_px: [0; 2],
567 visible: Vec::new(),
568 paint_obj: None,
569 mini_obj: None,
570 mini_ext: None,
571 store: Rc::new(RefCell::new(Backing::default())),
572 moving_until: 0,
573 gpu: CanvasGpu::Zoom,
574 id: 0,
575 }
576 }
577
578 pub fn request_fit(&mut self) {
580 self.fit_req = true;
581 }
582
583 pub fn busy(&self) -> bool {
585 !matches!(self.g, Gesture::Idle | Gesture::Hold)
586 }
587
588 pub fn route(&self, key: u64) -> Option<&DgRoute> {
590 let i = *self.ix.edges.get(&key)?;
591 self.ix.geo.routes.get(i as usize)
592 }
593
594 pub fn geo(&self) -> &Rc<DgGeo> {
596 &self.ix.geo
597 }
598
599 pub fn bounds(&self) -> Option<[f32; 4]> {
601 self.ix.bounds
602 }
603
604 pub fn visible(&self) -> &[u32] {
606 &self.visible
607 }
608
609 fn snaps(&self, mods: u32) -> bool {
610 self.snap && self.grid > 0.0 && mods & MOD_ALT == 0
611 }
612
613 fn snap_pt(&self, p: [f32; 2], mods: u32) -> [f32; 2] {
614 if self.snaps(mods) { [snapv(p[0], self.grid), snapv(p[1], self.grid)] } else { p }
615 }
616
617 fn node_index(&self, key: u64) -> Option<u32> {
619 self.ix.ports.get(&key).map(|p| p.0).or_else(|| self.ix.nodes.get(&key).copied())
620 }
621
622 fn node<'m>(&self, m: &DModel<'m>, key: u64) -> Option<&'m DNode> {
623 let i = *self.ix.nodes.get(&key)?;
624 m.nodes.get(i as usize)
625 }
626
627 fn prune(&mut self) -> bool {
629 let ix = &self.ix;
630 let n = self.sel.len();
631 self.sel.retain(|k| ix.nodes.contains_key(k) || ix.edges.contains_key(k));
632 if self.focus != 0 && !ix.nodes.contains_key(&self.focus) && !ix.edges.contains_key(&self.focus) {
633 self.focus = 0;
634 }
635 n != self.sel.len()
636 }
637
638 fn hit(&self, m: &DModel, p: [f32; 2], tool: u32, dpi: i32) -> DHit {
641 let s = self.vp.scale() as f32;
642 if tool == DG_PAN || !(s > 0.0) {
643 return DHit::None;
644 }
645 let w = |dip: i32| dp_at(dip, dpi) as f32 / s; if tool == DG_SELECT {
648 let hr = w(6);
649 for &k in &self.sel {
650 let Some(n) = self.node(m, k).filter(|n| n.flags & DN_RESIZE != 0) else { continue };
651 let c = [n.rect[0] + n.rect[2], n.rect[1] + n.rect[3]];
652 if fabs(p[0] - c[0]) <= hr && fabs(p[1] - c[1]) <= hr {
653 return DHit::Resize(k);
654 }
655 }
656 }
657 if tool == DG_SELECT {
659 let hr = w(6);
660 let mut best: Option<(f32, DHit)> = None;
661 for pass in 0..2 {
662 for &k in &self.sel {
663 let Some(&ei) = self.ix.edges.get(&k) else { continue };
664 let (Some(e), Some(rt)) = (m.edges.get(ei as usize), self.ix.geo.routes.get(ei as usize)) else { continue };
665 if pass == 0 {
666 for (j, q) in m.wps[range_of(&e.wps, m.wps.len())].iter().enumerate() {
667 let d = dist(p, *q);
668 if d <= hr && best.is_none_or(|b| d < b.0) {
669 best = Some((d, DHit::Waypoint(k, j as u32)));
670 }
671 }
672 } else {
673 for j in 0..rt.marks.len().saturating_sub(1) {
674 let Some(q) = mid_of(rt, j) else { continue };
675 let d = dist(p, q);
676 if d <= hr && best.is_none_or(|b| d < b.0) {
677 best = Some((d, DHit::Mid(k, j as u32)));
678 }
679 }
680 }
681 }
682 if let Some((_, h)) = best {
683 return h;
684 }
685 }
686 }
687 let pr = w(6);
689 let mut cand = Vec::new();
690 self.ix.query([p[0] - pr, p[1] - pr, p[0] + pr, p[1] + pr], false, m.nodes.len(), &mut cand);
691 for &i in cand.iter().rev() {
692 let Some(n) = m.nodes.get(i as usize) else { continue };
693 if n.flags & DN_NOSELECT != 0 {
694 continue;
695 }
696 let r = n.rect;
697 let within = |g: f32| p[0] >= r[0] - g && p[1] >= r[1] - g && p[0] < r[0] + r[2] + g && p[1] < r[1] + r[3] + g;
698 if !within(pr) {
699 continue;
700 }
701 if tool != DG_PLACE {
702 let mut best: Option<(f32, u64)> = None;
703 for q in &m.ports[range_of(&n.ports, m.ports.len())] {
704 if q.flags & DPT_HIDDEN != 0 {
705 continue;
706 }
707 let d = dist(p, [r[0] + q.at[0], r[1] + q.at[1]]);
708 if d <= pr && best.is_none_or(|b| d < b.0) {
709 best = Some((d, q.key));
710 }
711 }
712 if let Some((_, k)) = best {
713 return DHit::Port(k);
714 }
715 }
716 if n.flags & DN_FRAME != 0 {
717 let (b, bar) = (w(4), self.frame_bar.min(r[3]));
719 let on_bar = within(0.0) && p[1] < r[1] + bar;
720 let inner = p[0] >= r[0] + b && p[1] >= r[1] + b && p[0] < r[0] + r[2] - b && p[1] < r[1] + r[3] - b;
721 if !on_bar && !(within(b) && !inner) {
722 continue;
723 }
724 } else if n.flags & DN_HITMIN != 0 {
725 let min = w(HITMIN_DIP);
727 let (gx, gy) = (((min - r[2]) * 0.5).clamp(0.0, pr), ((min - r[3]) * 0.5).clamp(0.0, pr));
728 if !(p[0] >= r[0] - gx && p[1] >= r[1] - gy && p[0] < r[0] + r[2] + gx && p[1] < r[1] + r[3] + gy) {
729 continue;
730 }
731 } else if !within(0.0) {
732 continue;
733 }
734 if n.flags & DN_TOGGLE != 0 && tool == DG_SELECT {
735 let t = dg_toggle_rect(n, self.frame_bar);
736 if p[0] >= t[0] && p[1] >= t[1] && p[0] < t[0] + t[2] && p[1] < t[1] + t[3] {
737 return DHit::Toggle(n.key);
738 }
739 }
740 return DHit::Node(n.key);
741 }
742 if tool == DG_SELECT {
744 let tol = w(5).max((self.edge_width * s * 0.5 + dp_at(3, dpi) as f32) / s);
745 self.ix.query([p[0] - tol, p[1] - tol, p[0] + tol, p[1] + tol], true, m.edges.len(), &mut cand);
746 let mut best: Option<(f32, usize, usize, f32)> = None;
747 for &i in &cand {
748 let Some(rt) = self.ix.geo.routes.get(i as usize) else { continue };
749 if rt.pts.len() < 2 || !overlaps(rt.bb, [p[0] - tol, p[1] - tol, p[0] + tol, p[1] + tol]) {
750 continue;
751 }
752 let (d2, j, u) = near_route(rt, p);
753 if d2 <= tol * tol && best.is_none_or(|b| d2 <= b.0) {
754 best = Some((d2, i as usize, j, u));
755 }
756 }
757 if let Some((_, i, j, u)) = best {
758 let rt = &self.ix.geo.routes[i];
759 let (seg, t) = seg_t(rt, j, u);
760 return DHit::Edge { key: rt.key, seg, t };
761 }
762 }
763 DHit::None
764 }
765
766 fn cursor(&self, m: &DModel, tool: u32, space: bool) -> u32 {
767 match self.g {
768 Gesture::Pan { .. } => return CUR_HAND,
769 Gesture::Move { .. } | Gesture::Waypoint { .. } => return CUR_MOVE,
770 Gesture::Connect { .. } => return CUR_CROSS,
771 Gesture::Resize { .. } => return CUR_SIZE_NWSE,
772 _ => {}
773 }
774 if space || tool == DG_PAN {
775 return CUR_HAND;
776 }
777 match (tool, self.hover) {
778 (DG_PLACE, _) => CUR_CROSS,
779 (_, DHit::Toggle(_)) => CUR_ARROW,
780 (_, DHit::Resize(_)) => CUR_SIZE_NWSE,
781 (_, DHit::Port(_)) => CUR_CROSS,
782 (DG_CONNECT, DHit::Node(_)) => CUR_CROSS,
783 (_, DHit::Waypoint(..) | DHit::Mid(..)) => CUR_MOVE,
784 (_, DHit::Node(k)) if self.node(m, k).is_some_and(|n| n.flags & DN_LOCKED == 0) => CUR_MOVE,
785 _ => CUR_ARROW,
786 }
787 }
788
789 fn wheel(&mut self, wx: i32, wy: i32, mods: u32, at: [i32; 2], notch: i32) {
792 if wy != 0 && mods & MOD_CTRL != 0 {
793 let n = wy.unsigned_abs().div_ceil(120).min(16);
794 for _ in 0..n {
795 self.vp.step(if wy > 0 { 1 } else { -1 }, at[0] as f32 + 0.5, at[1] as f32 + 0.5);
796 }
797 return;
798 }
799 let k = notch as f64 / 120.0 / self.vp.scale();
801 let (dx, dy) = if mods & MOD_SHIFT != 0 { (wx - wy, 0) } else { (wx, -wy) };
802 self.vp.cx += dx as f64 * k;
803 self.vp.cy += dy as f64 * k;
804 }
805
806 fn step(&mut self, m: &DModel, i: &In, out: &mut Vec<DEv>) {
807 let g = core::mem::take(&mut self.g);
808 self.g = match g {
809 Gesture::Idle => self.idle(i, out),
810 Gesture::Press { hit, px, at, mods, was } => {
811 let far = (i.px[0] - px[0]).abs() > i.lim || (i.px[1] - px[1]).abs() > i.lim;
812 if i.btn & (1 << BTN_LEFT) == 0 {
813 self.click(m, i.tool, hit, at, mods, was, out)
814 } else if far {
815 self.last_hit = None; self.drag(m, i.tool, hit, at, mods)
817 } else {
818 Gesture::Press { hit, px, at, mods, was }
819 }
820 }
821 g => g,
822 };
823 let g = core::mem::take(&mut self.g);
824 self.g = self.follow(m, i, g, out);
825 }
826
827 fn idle(&mut self, i: &In, out: &mut Vec<DEv>) -> Gesture {
829 let hov = i.ev & EV_HOVER != 0;
830 let lpress = hov && i.pressed & (1 << BTN_LEFT) != 0 && i.mine;
831 let mpress = hov && i.pressed & (1 << BTN_MIDDLE) != 0;
832 if mpress || (lpress && (i.space || i.tool == DG_PAN)) {
833 let btn = if mpress { BTN_MIDDLE } else { BTN_LEFT };
834 return Gesture::Pan { btn, px: i.px, cx: self.vp.cx, cy: self.vp.cy };
835 }
836 if !lpress {
837 return Gesture::Idle;
838 }
839 let hit = self.hover;
842 let near = (i.px[0] - self.last_px[0]).abs() <= i.lim * 2 && (i.px[1] - self.last_px[1]).abs() <= i.lim * 2;
843 if self.last_hit.is_some_and(|l| same(hit, l)) && near && i.now.wrapping_sub(self.last_ms) <= i.dbl {
844 self.last_hit = None;
845 match hit {
846 DHit::Waypoint(e, k) => {
847 out.push(DEv::WaypointDelete { edge: e, idx: k });
848 return Gesture::Hold;
849 }
850 DHit::None if i.tool == DG_SELECT && i.mods & (MOD_SHIFT | MOD_CTRL) == 0 => {
851 out.push(DEv::DoubleClick { at: i.p });
852 return Gesture::Hold;
853 }
854 DHit::None | DHit::Mid(..) | DHit::Toggle(_) | DHit::Resize(_) => {}
856 h => {
857 out.push(DEv::Activate(h));
858 return Gesture::Hold;
859 }
860 }
861 } else {
862 self.last_hit = Some(hit);
863 self.last_ms = i.now;
864 self.last_px = i.px;
865 }
866 let mut was = false;
867 if i.tool == DG_SELECT {
868 let k = match hit {
869 DHit::Node(k) => k,
870 DHit::Edge { key, .. } => key,
871 _ => 0,
872 };
873 if k != 0 {
874 was = self.sel.contains(&k);
875 let changed = if i.mods & MOD_CTRL != 0 {
876 if was {
877 self.sel.remove(&k);
878 } else {
879 self.sel.insert(k);
880 }
881 true
882 } else if i.mods & MOD_SHIFT != 0 {
883 self.sel.insert(k)
884 } else if !was {
885 self.sel.clear();
886 self.sel.insert(k);
887 true
888 } else {
889 false
890 };
891 if changed {
892 out.push(DEv::Select);
893 }
894 if matches!(hit, DHit::Node(_)) {
895 self.focus = k;
896 }
897 }
898 }
899 Gesture::Press { hit, px: i.px, at: i.p, mods: i.mods, was }
900 }
901
902 #[allow(clippy::too_many_arguments)]
904 fn click(&mut self, m: &DModel, tool: u32, hit: DHit, at: [f32; 2], mods: u32, was: bool, out: &mut Vec<DEv>) -> Gesture {
905 match tool {
906 DG_PLACE => {
907 out.push(DEv::Click { at: self.snap_pt(at, mods) });
908 Gesture::Idle
909 }
910 DG_CONNECT => match hit {
911 DHit::Node(k) | DHit::Port(k) => Gesture::Connect { from: k, quick: false, armed: true, at, over: 0 },
912 _ => {
913 out.push(DEv::Click { at });
914 Gesture::Idle
915 }
916 },
917 _ => {
918 match hit {
919 DHit::Node(k) | DHit::Edge { key: k, .. } => {
921 if mods & (MOD_SHIFT | MOD_CTRL) == 0 && was && self.sel.len() > 1 {
922 self.sel.clear();
923 self.sel.insert(k);
924 out.push(DEv::Select);
925 }
926 }
927 DHit::Port(k) => {
929 let nk = self.node_index(k).and_then(|n| m.nodes.get(n as usize)).map_or(0, |n| n.key);
930 if nk != 0 {
931 let before = self.sel.clone();
932 if mods & MOD_CTRL != 0 {
933 if !self.sel.remove(&nk) {
934 self.sel.insert(nk);
935 }
936 } else {
937 if mods & MOD_SHIFT == 0 {
938 self.sel.clear();
939 }
940 self.sel.insert(nk);
941 }
942 if before != self.sel {
943 out.push(DEv::Select);
944 }
945 self.focus = nk;
946 }
947 }
948 DHit::None => {
949 if mods & (MOD_SHIFT | MOD_CTRL) == 0 && !self.sel.is_empty() {
950 self.sel.clear();
951 out.push(DEv::Select);
952 }
953 out.push(DEv::Click { at });
954 }
955 DHit::Toggle(k) => out.push(DEv::Toggle(k)),
956 _ => {}
957 }
958 Gesture::Idle
959 }
960 }
961 }
962
963 fn drag(&mut self, m: &DModel, tool: u32, hit: DHit, at: [f32; 2], mods: u32) -> Gesture {
965 match tool {
966 DG_PLACE => Gesture::Hold,
967 DG_CONNECT => match hit {
968 DHit::Node(k) | DHit::Port(k) => Gesture::Connect { from: k, quick: false, armed: false, at, over: 0 },
969 _ => Gesture::Hold,
970 },
971 _ => match hit {
972 DHit::Node(k) if self.sel.contains(&k) => {
973 let movable = |key: u64| self.node(m, key).is_some_and(|n| n.flags & DN_LOCKED == 0);
974 let keys: Vec<u64> = self.sel.iter().copied().filter(|&s| movable(s)).collect();
975 let Some(&first) = keys.first() else { return Gesture::Hold };
976 let a = if movable(k) { k } else { first };
977 let anchor = self.node(m, a).map_or([0.0; 2], |n| [n.rect[0], n.rect[1]]);
978 let step = self.node(m, a).is_some_and(|n| n.flags & DN_STEP != 0);
979 Gesture::Move { keys, start: at, anchor, sent: [0.0; 2], step }
980 }
981 DHit::Resize(k) => match self.node(m, k) {
982 Some(n) => Gesture::Resize { key: k, orig: n.rect, start: at, at: n.rect, started: false },
983 None => Gesture::Hold,
984 },
985 DHit::Port(k) => Gesture::Connect { from: k, quick: true, armed: false, at, over: 0 },
986 DHit::Waypoint(e, idx) => {
987 let orig = self.waypoint(m, e, idx).unwrap_or(at);
988 Gesture::Waypoint { edge: e, idx, orig, insert: false, started: false, at: orig }
989 }
990 DHit::Mid(e, idx) => Gesture::Waypoint { edge: e, idx, orig: at, insert: true, started: false, at },
991 DHit::None => {
992 let base = if mods & (MOD_SHIFT | MOD_CTRL) != 0 { self.sel.clone() } else { BTreeSet::new() };
993 Gesture::Marquee { start: at, cur: at, mods, base }
994 }
995 _ => Gesture::Hold,
996 },
997 }
998 }
999
1000 fn waypoint(&self, m: &DModel, edge: u64, idx: u32) -> Option<[f32; 2]> {
1001 let e = m.edges.get(*self.ix.edges.get(&edge)? as usize)?;
1002 m.wps[range_of(&e.wps, m.wps.len())].get(idx as usize).copied()
1003 }
1004
1005 fn target(&self, from: u64) -> u64 {
1007 let src = self.node_index(from);
1008 match self.hover {
1009 DHit::Port(k) if k != from && self.node_index(k) != src => k,
1010 DHit::Node(k) if self.node_index(k) != src => k,
1011 _ => 0,
1012 }
1013 }
1014
1015 fn follow(&mut self, m: &DModel, i: &In, g: Gesture, out: &mut Vec<DEv>) -> Gesture {
1017 let held = |b: u32| i.btn & (1 << b) != 0;
1018 match g {
1019 Gesture::Pan { btn, px, cx, cy } => {
1020 if !held(btn) {
1021 return Gesture::Idle;
1022 }
1023 let s = self.vp.scale();
1024 self.vp.cx = cx - (i.px[0] - px[0]) as f64 / s;
1025 self.vp.cy = cy - (i.px[1] - px[1]) as f64 / s;
1026 Gesture::Pan { btn, px, cx, cy }
1027 }
1028 Gesture::Hold => {
1029 if held(BTN_LEFT) {
1030 Gesture::Hold
1031 } else {
1032 Gesture::Idle
1033 }
1034 }
1035 Gesture::Move { keys, start, anchor, sent, step } => {
1036 let raw = [i.p[0] - start[0], i.p[1] - start[1]];
1037 let tot = if self.snaps(i.mods) && step {
1038 [snapv(raw[0], self.grid), snapv(raw[1], self.grid)]
1039 } else if self.snaps(i.mods) {
1040 [snapv(anchor[0] + raw[0], self.grid) - anchor[0], snapv(anchor[1] + raw[1], self.grid) - anchor[1]]
1041 } else {
1042 raw
1043 };
1044 let done = !held(BTN_LEFT);
1045 let d = [tot[0] - sent[0], tot[1] - sent[1]];
1046 if d != [0.0, 0.0] || done {
1047 out.push(DEv::Move { keys: keys.clone(), d, total: tot, done });
1048 }
1049 if done { Gesture::Idle } else { Gesture::Move { keys, start, anchor, sent: tot, step } }
1050 }
1051 Gesture::Resize { key, orig, start, at, started } => {
1052 let rect = [orig[0], orig[1], (orig[2] + i.p[0] - start[0]).max(8.0), (orig[3] + i.p[1] - start[1]).max(8.0)];
1053 let done = !held(BTN_LEFT);
1054 if rect != at || done {
1055 out.push(DEv::Resize { key, rect, done });
1056 }
1057 if done { Gesture::Idle } else { Gesture::Resize { key, orig, start, at: rect, started: started || rect != orig } }
1058 }
1059 Gesture::Marquee { start, mods, base, .. } => {
1060 let cur = i.p;
1061 let rr = [start[0].min(cur[0]), start[1].min(cur[1]), start[0].max(cur[0]), start[1].max(cur[1])];
1062 let toggle = mods & MOD_CTRL != 0;
1063 let mut sel = base.clone();
1064 let mut flip = |k: u64| {
1065 if toggle && sel.contains(&k) {
1066 sel.remove(&k);
1067 } else {
1068 sel.insert(k);
1069 }
1070 };
1071 let mut v = Vec::new();
1072 self.ix.query(rr, false, m.nodes.len(), &mut v);
1073 for &n in &v {
1074 if let Some(n) = m.nodes.get(n as usize) {
1075 if n.flags & DN_NOSELECT == 0 && inside(xyxy(n.rect), rr) {
1076 flip(n.key);
1077 }
1078 }
1079 }
1080 self.ix.query(rr, true, m.edges.len(), &mut v);
1081 for &e in &v {
1082 if let Some(rt) = self.ix.geo.routes.get(e as usize) {
1083 if rt.pts.len() >= 2 && inside(rt.bb, rr) {
1084 flip(rt.key);
1085 }
1086 }
1087 }
1088 if sel != self.sel {
1089 self.sel = sel;
1090 out.push(DEv::Select);
1091 }
1092 if held(BTN_LEFT) { Gesture::Marquee { start, cur, mods, base } } else { Gesture::Idle }
1093 }
1094 Gesture::Connect { from, quick, armed, at, over } => {
1095 let o = self.target(from);
1096 let a = i.p;
1097 if a != at || o != over {
1098 out.push(DEv::ConnectDrag { from, at: a, over: o });
1099 }
1100 let lpress = i.pressed & (1 << BTN_LEFT) != 0;
1101 let end = if armed { lpress } else { !held(BTN_LEFT) };
1102 if end {
1103 let ours = !armed || (i.mine && i.ev & EV_HOVER != 0);
1105 if o != 0 && ours {
1106 out.push(DEv::Connect { from, to: o, quick });
1107 } else {
1108 out.push(DEv::ConnectCancel);
1109 }
1110 return if armed { Gesture::Hold } else { Gesture::Idle };
1111 }
1112 Gesture::Connect { from, quick, armed, at: a, over: o }
1113 }
1114 Gesture::Waypoint { edge, idx, orig, insert, started, at } => {
1115 let a = self.snap_pt(i.p, i.mods);
1116 let done = !held(BTN_LEFT);
1117 if a != at || !started || done {
1118 if started || done || insert || a != orig {
1119 out.push(DEv::Waypoint { edge, idx, at: a, insert: insert && !started, done });
1120 } else {
1121 return Gesture::Waypoint { edge, idx, orig, insert, started, at };
1122 }
1123 }
1124 if done { Gesture::Idle } else { Gesture::Waypoint { edge, idx, orig, insert, started: true, at: a } }
1125 }
1126 g => g,
1127 }
1128 }
1129
1130 fn cancel(&mut self, out: &mut Vec<DEv>) -> bool {
1132 let g = core::mem::take(&mut self.g);
1133 self.g = match g {
1134 Gesture::Idle => return false,
1135 Gesture::Press { .. } | Gesture::Hold => Gesture::Hold,
1136 Gesture::Pan { cx, cy, .. } => {
1137 self.vp.cx = cx;
1138 self.vp.cy = cy;
1139 Gesture::Idle
1140 }
1141 Gesture::Move { keys, sent, .. } => {
1142 if sent != [0.0, 0.0] {
1143 out.push(DEv::Move { keys, d: [-sent[0], -sent[1]], total: [0.0; 2], done: true });
1144 }
1145 Gesture::Hold
1146 }
1147 Gesture::Resize { key, orig, started, .. } => {
1148 if started {
1149 out.push(DEv::Resize { key, rect: orig, done: true });
1150 }
1151 Gesture::Hold
1152 }
1153 Gesture::Marquee { base, .. } => {
1154 if self.sel != base {
1155 self.sel = base;
1156 out.push(DEv::Select);
1157 }
1158 Gesture::Hold
1159 }
1160 Gesture::Connect { armed, .. } => {
1161 out.push(DEv::ConnectCancel);
1162 if armed { Gesture::Idle } else { Gesture::Hold }
1163 }
1164 Gesture::Waypoint { edge, idx, orig, insert, started, .. } => {
1165 if started && insert {
1166 out.push(DEv::WaypointDelete { edge, idx });
1167 } else if started {
1168 out.push(DEv::Waypoint { edge, idx, at: orig, insert: false, done: true });
1169 }
1170 Gesture::Hold
1171 }
1172 };
1173 true
1174 }
1175
1176 fn context(&mut self, hit: DHit, at: [i32; 2], out: &mut Vec<DEv>) {
1179 let hit = if let DHit::Toggle(k) = hit { DHit::Node(k) } else { hit };
1180 if let DHit::Node(k) | DHit::Edge { key: k, .. } = hit {
1181 if !self.sel.contains(&k) {
1182 self.sel.clear();
1183 self.sel.insert(k);
1184 out.push(DEv::Select);
1185 }
1186 if matches!(hit, DHit::Node(_)) {
1187 self.focus = k;
1188 }
1189 }
1190 out.push(DEv::Context(hit, at));
1191 }
1192
1193 fn walk_keys(&self, m: &DModel) -> Vec<u64> {
1195 m.nodes.iter().filter(|n| n.flags & DN_NOSELECT == 0).map(|n| n.key).collect()
1196 }
1197
1198 fn tab_keys(&self, m: &DModel) -> Vec<u64> {
1200 let mut keys = self.walk_keys(m);
1201 if self.tab_edges {
1202 keys.extend(self.ix.geo.routes.iter().filter(|rt| rt.pts.len() >= 2).map(|rt| rt.key));
1203 }
1204 keys
1205 }
1206
1207 fn reveal(&mut self, m: &DModel, key: u64) {
1209 let r = match self.node(m, key) {
1210 Some(n) => xyxy(n.rect),
1211 None => match self.route(key) {
1212 Some(rt) if rt.pts.len() >= 2 => {
1213 let c = dg_point_at(&rt.pts, 0.5);
1214 [c[0] - 8.0, c[1] - 8.0, c[0] + 8.0, c[1] + 8.0]
1215 }
1216 _ => return,
1217 },
1218 };
1219 if !inside(r, self.vp.world_rect()) {
1220 self.vp.cx = (r[0] as f64 + r[2] as f64) * 0.5;
1221 self.vp.cy = (r[1] as f64 + r[3] as f64) * 0.5;
1222 }
1223 }
1224
1225 fn movable_sel(&self, m: &DModel) -> Vec<u64> {
1226 self.sel.iter().copied().filter(|&k| self.node(m, k).is_some_and(|n| n.flags & DN_LOCKED == 0)).collect()
1227 }
1228}
1229
1230struct In {
1232 tool: u32,
1233 mods: u32,
1234 space: bool,
1235 ev: u32,
1236 px: [i32; 2],
1237 p: [f32; 2],
1238 btn: u32,
1239 pressed: u32,
1240 mine: bool,
1241 lim: i32,
1242 now: u32,
1243 dbl: u32,
1244}
1245
1246fn cells_of(r: [f32; 4]) -> (i32, i32, i32, i32) {
1249 (fl(r[0] / CELL), fl(r[1] / CELL), fl(r[2] / CELL), fl(r[3] / CELL))
1250}
1251
1252impl Index {
1253 fn update(&mut self, m: &DModel) -> bool {
1255 let counts = [m.nodes.len(), m.ports.len(), m.edges.len(), m.wps.len()];
1256 if self.built && self.ver == m.ver && self.counts == counts {
1257 return false;
1258 }
1259 self.built = true;
1260 self.ver = m.ver;
1261 self.counts = counts;
1262 self.nodes.clear();
1263 self.ports.clear();
1264 self.edges.clear();
1265 for (i, n) in m.nodes.iter().enumerate() {
1266 self.nodes.insert(n.key, i as u32);
1267 for p in range_of(&n.ports, m.ports.len()) {
1268 self.ports.insert(m.ports[p].key, (i as u32, p as u32));
1269 }
1270 }
1271 for (i, e) in m.edges.iter().enumerate() {
1272 self.edges.insert(e.key, i as u32);
1273 }
1274 let mut geo = DgGeo { routes: Vec::with_capacity(m.edges.len()), counts: (m.nodes.len(), m.edges.len()), ..DgGeo::default() };
1275 for e in m.edges {
1276 let mut r = DgRoute::default();
1277 route(m, Some(self), e, &mut r);
1278 geo.routes.push(r);
1279 }
1280 let mut b = None;
1281 for (i, n) in m.nodes.iter().enumerate() {
1282 let r = xyxy(n.rect);
1283 geo.put(r, i as u32, false);
1284 b = union(b, r);
1285 }
1286 for i in 0..geo.routes.len() {
1287 let rt = &geo.routes[i];
1288 if rt.pts.len() >= 2 {
1289 let bb = rt.bb;
1290 geo.put(bb, i as u32, true);
1291 b = union(b, bb);
1292 }
1293 }
1294 self.bounds = b;
1295 self.geo = Rc::new(geo);
1296 true
1297 }
1298
1299 fn query(&self, r: [f32; 4], edge: bool, all: usize, out: &mut Vec<u32>) {
1302 self.geo.find_in(r, edge, all, out);
1303 }
1304}
1305
1306impl DgGeo {
1307 fn put(&mut self, r: [f32; 4], i: u32, edge: bool) {
1308 let (c0, r0, c1, r1) = cells_of(r);
1309 let n = (c1 as i64 - c0 as i64 + 1) * (r1 as i64 - r0 as i64 + 1);
1310 if !(1..=SPAN_MAX).contains(&n) {
1311 if edge { self.big.1.push(i) } else { self.big.0.push(i) }
1312 return;
1313 }
1314 for y in r0..=r1 {
1315 for x in c0..=c1 {
1316 let c = self.cells.entry((y, x)).or_default();
1317 if edge { c.1.push(i) } else { c.0.push(i) }
1318 }
1319 }
1320 }
1321
1322 pub fn find(&self, r: [f32; 4], edges: bool, out: &mut Vec<u32>) {
1325 let all = if edges { self.counts.1 } else { self.counts.0 };
1326 self.find_in(r, edges, all, out);
1327 }
1328
1329 fn find_in(&self, r: [f32; 4], edge: bool, all: usize, out: &mut Vec<u32>) {
1330 out.clear();
1331 let (c0, r0, c1, r1) = cells_of(r);
1332 let n = (c1 as i64 - c0 as i64 + 1) * (r1 as i64 - r0 as i64 + 1);
1333 if !(1..=CELLS_MAX).contains(&n) {
1334 out.extend(0..all as u32);
1335 return;
1336 }
1337 for y in r0..=r1 {
1338 for (_, c) in self.cells.range((y, c0)..=(y, c1)) {
1339 out.extend_from_slice(if edge { &c.1 } else { &c.0 });
1340 }
1341 }
1342 out.extend_from_slice(if edge { &self.big.1 } else { &self.big.0 });
1343 out.sort_unstable();
1344 out.dedup();
1345 }
1346}
1347
1348#[derive(Clone, Copy)]
1350enum Ep {
1351 Port([f32; 2], u8),
1352 Node([f32; 4]),
1353}
1354
1355fn ep_of(m: &DModel, ix: Option<&Index>, key: u64) -> Option<Ep> {
1356 let (ni, pi) = match ix {
1357 Some(ix) => match ix.ports.get(&key) {
1358 Some(&(n, p)) => (n as usize, Some(p as usize)),
1359 None => (*ix.nodes.get(&key)? as usize, None),
1360 },
1361 None => {
1362 let mut f = None;
1363 for (i, n) in m.nodes.iter().enumerate() {
1364 if n.key == key {
1365 f = Some((i, None));
1366 break;
1367 }
1368 if let Some(p) = range_of(&n.ports, m.ports.len()).find(|&p| m.ports[p].key == key) {
1369 f = Some((i, Some(p)));
1370 break;
1371 }
1372 }
1373 f?
1374 }
1375 };
1376 let n = m.nodes.get(ni)?;
1377 match pi {
1378 Some(p) => {
1379 let q = m.ports.get(p)?;
1380 Some(Ep::Port([n.rect[0] + q.at[0], n.rect[1] + q.at[1]], port_side(n.rect, q.at)))
1381 }
1382 None => Some(Ep::Node(n.rect)),
1383 }
1384}
1385
1386fn port_side(r: [f32; 4], at: [f32; 2]) -> u8 {
1388 let d = [fabs(at[0]), fabs(r[2] - at[0]), fabs(at[1]), fabs(r[3] - at[1])];
1389 let mut s = 0;
1390 for k in 1..4 {
1391 if d[k] < d[s] {
1392 s = k;
1393 }
1394 }
1395 [S_L, S_R, S_T, S_B][s]
1396}
1397
1398fn side_vec(s: u8) -> [f32; 2] {
1399 match s {
1400 S_L => [-1.0, 0.0],
1401 S_R => [1.0, 0.0],
1402 S_T => [0.0, -1.0],
1403 S_B => [0.0, 1.0],
1404 _ => [0.0, 0.0],
1405 }
1406}
1407
1408fn ep_point(e: Ep) -> [f32; 2] {
1409 match e {
1410 Ep::Port(p, _) => p,
1411 Ep::Node(r) => [r[0] + r[2] * 0.5, r[1] + r[3] * 0.5],
1412 }
1413}
1414
1415fn anchor(e: Ep, toward: [f32; 2], ortho: bool) -> ([f32; 2], u8) {
1418 match e {
1419 Ep::Port(p, s) => (p, s),
1420 Ep::Node(r) => {
1421 let c = [r[0] + r[2] * 0.5, r[1] + r[3] * 0.5];
1422 let (hw, hh) = (r[2] * 0.5, r[3] * 0.5);
1423 let v = [toward[0] - c[0], toward[1] - c[1]];
1424 if fabs(v[0]) < EPS && fabs(v[1]) < EPS {
1425 return (c, S_NONE);
1426 }
1427 let horiz = fabs(v[0]) * hh >= fabs(v[1]) * hw;
1428 let s = if horiz {
1429 if v[0] < 0.0 { S_L } else { S_R }
1430 } else if v[1] < 0.0 {
1431 S_T
1432 } else {
1433 S_B
1434 };
1435 if ortho {
1436 let d = side_vec(s);
1437 return ([c[0] + d[0] * hw, c[1] + d[1] * hh], s);
1438 }
1439 let kx = if fabs(v[0]) > EPS { hw / fabs(v[0]) } else { f32::MAX };
1440 let ky = if fabs(v[1]) > EPS { hh / fabs(v[1]) } else { f32::MAX };
1441 let k = kx.min(ky);
1442 ([c[0] + v[0] * k, c[1] + v[1] * k], s)
1443 }
1444 }
1445}
1446
1447fn route(m: &DModel, ix: Option<&Index>, e: &DEdge, out: &mut DgRoute) {
1449 out.key = e.key;
1450 out.pts.clear();
1451 out.marks.clear();
1452 out.bb = [0.0; 4];
1453 let (Some(ea), Some(eb)) = (ep_of(m, ix, e.a), ep_of(m, ix, e.b)) else { return };
1454 let wps = &m.wps[range_of(&e.wps, m.wps.len())];
1455 let ta = wps.first().copied().unwrap_or(ep_point(eb));
1456 let tb = wps.last().copied().unwrap_or(ep_point(ea));
1457 let ortho = e.route == DR_ORTHO;
1458 let (pa, sa) = anchor(ea, ta, ortho);
1459 let (pb, sb) = anchor(eb, tb, ortho);
1460 match e.route {
1461 DR_BEZIER => bezier(pa, sa, wps, pb, sb, out),
1462 DR_ORTHO => ortho_route(pa, sa, wps, pb, sb, out),
1463 _ => {
1464 out.pts.push(pa);
1465 out.pts.extend_from_slice(wps);
1466 out.pts.push(pb);
1467 out.marks.extend(0..out.pts.len() as u32);
1468 }
1469 }
1470 let mut bb = [f32::MAX, f32::MAX, f32::MIN, f32::MIN];
1471 for p in &out.pts {
1472 bb = [bb[0].min(p[0]), bb[1].min(p[1]), bb[2].max(p[0]), bb[3].max(p[1])];
1473 }
1474 out.bb = bb;
1475}
1476
1477fn bezier(pa: [f32; 2], sa: u8, wps: &[[f32; 2]], pb: [f32; 2], sb: u8, out: &mut DgRoute) {
1480 let mut p = Vec::with_capacity(wps.len() + 2);
1481 p.push(pa);
1482 p.extend_from_slice(wps);
1483 p.push(pb);
1484 let n = p.len();
1485 let tan = |i: usize| -> [f32; 2] {
1487 let (a, b) = (p[i.saturating_sub(1)], p[(i + 1).min(n - 1)]);
1488 let k = if i == 0 || i == n - 1 { 1.0 / 3.0 } else { 1.0 / 6.0 };
1489 [(b[0] - a[0]) * k, (b[1] - a[1]) * k]
1490 };
1491 out.pts.push(pa);
1492 out.marks.push(0);
1493 for i in 0..n - 1 {
1494 let (p0, p3) = (p[i], p[i + 1]);
1495 let reach = (dist(p0, p3) * 0.5).clamp(12.0, 200.0);
1496 let c1 = if i == 0 && sa != S_NONE {
1497 let v = side_vec(sa);
1498 [p0[0] + v[0] * reach, p0[1] + v[1] * reach]
1499 } else {
1500 let t = tan(i);
1501 [p0[0] + t[0], p0[1] + t[1]]
1502 };
1503 let c2 = if i == n - 2 && sb != S_NONE {
1504 let v = side_vec(sb);
1505 [p3[0] + v[0] * reach, p3[1] + v[1] * reach]
1506 } else {
1507 let t = tan(i + 1);
1508 [p3[0] - t[0], p3[1] - t[1]]
1509 };
1510 let len = dist(p0, c1) + dist(c1, c2) + dist(c2, p3);
1511 let steps = ((len / 6.0) as i32).clamp(4, 64);
1512 for s in 1..=steps {
1513 let t = s as f32 / steps as f32;
1514 let u = 1.0 - t;
1515 let (a, b, c, d) = (u * u * u, 3.0 * u * u * t, 3.0 * u * t * t, t * t * t);
1516 out.pts.push([
1517 a * p0[0] + b * c1[0] + c * c2[0] + d * p3[0],
1518 a * p0[1] + b * c1[1] + c * c2[1] + d * p3[1],
1519 ]);
1520 }
1521 out.marks.push(out.pts.len() as u32 - 1);
1522 }
1523}
1524
1525const AX_H: u8 = 0;
1526const AX_V: u8 = 1;
1527
1528fn axis_of(s: u8) -> u8 {
1529 if s == S_T || s == S_B { AX_V } else { AX_H }
1530}
1531
1532fn ortho_route(pa: [f32; 2], sa: u8, wps: &[[f32; 2]], pb: [f32; 2], sb: u8, out: &mut DgRoute) {
1535 let va = side_vec(sa);
1536 let vb = side_vec(sb);
1537 let a1 = [pa[0] + va[0] * STUB, pa[1] + va[1] * STUB];
1538 let b1 = [pb[0] + vb[0] * STUB, pb[1] + vb[1] * STUB];
1539 let mut def = Vec::with_capacity(wps.len() + 2); def.push(a1);
1541 def.extend_from_slice(wps);
1542 def.push(b1);
1543 let mut v: Vec<([f32; 2], bool)> = Vec::with_capacity(def.len() * 3 + 2);
1545 v.push((pa, true));
1546 v.push((a1, false));
1547 let q = def[1];
1549 let mut ax = if sa != S_NONE {
1550 let back = (q[0] - a1[0]) * va[0] + (q[1] - a1[1]) * va[1] < 0.0;
1551 if back { axis_of(sa) ^ 1 } else { axis_of(sa) }
1552 } else if fabs(q[0] - a1[0]) >= fabs(q[1] - a1[1]) {
1553 AX_H
1554 } else {
1555 AX_V
1556 };
1557 for i in 0..def.len() - 1 {
1558 let (p, q) = (def[i], def[i + 1]);
1559 let last = i + 2 == def.len();
1560 let (dx, dy) = (q[0] - p[0], q[1] - p[1]);
1561 if fabs(dx) < EPS || fabs(dy) < EPS {
1562 ax = if fabs(dx) < EPS { AX_H } else { AX_V };
1563 } else if last && sb != S_NONE {
1564 let mut e = axis_of(sb);
1566 if (p[0] - b1[0]) * vb[0] + (p[1] - b1[1]) * vb[1] < 0.0 {
1567 e ^= 1;
1568 }
1569 if ax != e {
1570 v.push((if ax == AX_H { [q[0], p[1]] } else { [p[0], q[1]] }, false));
1571 } else if ax == AX_H {
1572 let mx = (p[0] + q[0]) * 0.5;
1573 v.push(([mx, p[1]], false));
1574 v.push(([mx, q[1]], false));
1575 } else {
1576 let my = (p[1] + q[1]) * 0.5;
1577 v.push(([p[0], my], false));
1578 v.push(([q[0], my], false));
1579 }
1580 } else {
1581 v.push((if ax == AX_H { [q[0], p[1]] } else { [p[0], q[1]] }, false));
1582 }
1583 v.push((q, !last));
1584 }
1585 v.push((pb, true));
1586 let mut s: Vec<([f32; 2], bool)> = Vec::with_capacity(v.len());
1588 for (p, d) in v {
1589 if let Some(l) = s.last_mut() {
1590 if fabs(l.0[0] - p[0]) < EPS && fabs(l.0[1] - p[1]) < EPS {
1591 l.1 |= d;
1592 continue;
1593 }
1594 }
1595 if s.len() >= 2 {
1596 let (a, b) = (s[s.len() - 2].0, s[s.len() - 1]);
1597 let straight = (fabs(a[0] - b.0[0]) < EPS && fabs(b.0[0] - p[0]) < EPS) || (fabs(a[1] - b.0[1]) < EPS && fabs(b.0[1] - p[1]) < EPS);
1598 if straight && !b.1 {
1599 s.pop();
1600 }
1601 }
1602 s.push((p, d));
1603 }
1604 for (i, (p, d)) in s.iter().enumerate() {
1605 out.pts.push(*p);
1606 if *d || i == 0 || i + 1 == s.len() {
1607 out.marks.push(i as u32);
1608 }
1609 }
1610 out.marks.dedup();
1611}
1612
1613fn near_route(r: &DgRoute, p: [f32; 2]) -> (f32, usize, f32) {
1615 let mut best = (f32::MAX, 0, 0.0);
1616 for j in 0..r.pts.len().saturating_sub(1) {
1617 let (d2, u) = seg_dist2(p, r.pts[j], r.pts[j + 1]);
1618 if d2 < best.0 {
1619 best = (d2, j, u);
1620 }
1621 }
1622 best
1623}
1624
1625fn seg_dist2(p: [f32; 2], a: [f32; 2], b: [f32; 2]) -> (f32, f32) {
1626 let (vx, vy) = (b[0] - a[0], b[1] - a[1]);
1627 let l2 = vx * vx + vy * vy;
1628 let u = if l2 > 0.0 { (((p[0] - a[0]) * vx + (p[1] - a[1]) * vy) / l2).clamp(0.0, 1.0) } else { 0.0 };
1629 let (dx, dy) = (a[0] + vx * u - p[0], a[1] + vy * u - p[1]);
1630 (dx * dx + dy * dy, u)
1631}
1632
1633fn seg_t(r: &DgRoute, j: usize, u: f32) -> (u32, f32) {
1635 let mut seg = 0;
1636 while seg + 2 < r.marks.len() && r.marks[seg + 1] as usize <= j {
1637 seg += 1;
1638 }
1639 let (a, b) = (r.marks.get(seg).copied().unwrap_or(0) as usize, r.marks.get(seg + 1).copied().unwrap_or(0) as usize);
1640 if b <= a || b >= r.pts.len() {
1641 return (seg as u32, 0.0);
1642 }
1643 let total = dg_length(&r.pts[a..=b]);
1644 let mut at = 0.0;
1645 for k in a..j.min(b) {
1646 at += dist(r.pts[k], r.pts[k + 1]);
1647 }
1648 if j < b {
1649 at += dist(r.pts[j], r.pts[j + 1]) * u;
1650 }
1651 (seg as u32, if total > 0.0 { (at / total).clamp(0.0, 1.0) } else { 0.0 })
1652}
1653
1654fn mid_of(r: &DgRoute, i: usize) -> Option<[f32; 2]> {
1656 let (a, b) = (*r.marks.get(i)? as usize, *r.marks.get(i + 1)? as usize);
1657 if b <= a || b >= r.pts.len() {
1658 return None;
1659 }
1660 Some(dg_point_at(&r.pts[a..=b], 0.5))
1661}
1662
1663pub fn dg_length(pts: &[[f32; 2]]) -> f32 {
1665 pts.windows(2).map(|w| dist(w[0], w[1])).sum()
1666}
1667
1668pub fn dg_point_at(pts: &[[f32; 2]], f: f32) -> [f32; 2] {
1670 let Some(&first) = pts.first() else { return [0.0; 2] };
1671 let total = dg_length(pts);
1672 if !(total > 0.0) {
1673 return first;
1674 }
1675 let mut left = total * f.clamp(0.0, 1.0);
1676 for w in pts.windows(2) {
1677 let l = dist(w[0], w[1]);
1678 if left <= l && l > 0.0 {
1679 let u = left / l;
1680 return [w[0][0] + (w[1][0] - w[0][0]) * u, w[0][1] + (w[1][1] - w[0][1]) * u];
1681 }
1682 left -= l;
1683 }
1684 pts[pts.len() - 1]
1685}
1686
1687pub fn dg_toggle_rect(n: &DNode, bar: f32) -> [f32; 4] {
1691 let r = n.rect;
1692 let s = if n.flags & DN_FRAME != 0 { bar.min(r[3]) } else { bar.min(r[2]).min(r[3]) };
1693 [r[0] + r[2] - s, r[1], s, s]
1694}
1695
1696pub fn dg_edge_points(m: &DModel, i: usize, out: &mut Vec<[f32; 2]>) {
1699 out.clear();
1700 let Some(e) = m.edges.get(i) else { return };
1701 let mut r = DgRoute::default();
1702 route(m, None, e, &mut r);
1703 out.extend_from_slice(&r.pts);
1704}
1705
1706pub fn dg_paint_grid(g: &mut Gfx, vp: &Viewport, step: f32, minor: u32, major: u32) {
1711 if !(step > 0.0) {
1712 return;
1713 }
1714 let s = vp.scale() as f32;
1715 let mut st = step;
1716 let mut k = 0;
1717 while st * s < 8.0 && k < 12 {
1718 st *= 5.0;
1719 k += 1;
1720 }
1721 let r = vp.rect;
1722 let c = g.dc.clip;
1723 let (x0, y0) = (c[0].max(r[0]), c[1].max(r[1]));
1724 let (x1, y1) = (c[2].min(r[0] + r[2]), c[3].min(r[1] + r[3]));
1725 if x1 <= x0 || y1 <= y0 {
1726 return;
1727 }
1728 let a = vp.to_world(x0 as f32, y0 as f32);
1729 let b = vp.to_world(x1 as f32, y1 as f32);
1730 let hair = ((vp.dpi + 24) / 96).max(1);
1731 let (i0, i1) = (fl(a[0] / st), fl(b[0] / st) + 1);
1732 let (j0, j1) = (fl(a[1] / st), fl(b[1] / st) + 1);
1733 if i1 as i64 - i0 as i64 > 4000 || j1 as i64 - j0 as i64 > 4000 {
1734 return;
1735 }
1736 for pass in 0..2 {
1737 let want = pass == 1;
1739 g.dc.set_color(if want { major } else { minor });
1740 for i in i0..=i1 {
1741 if (i.rem_euclid(5) == 0) != want {
1742 continue;
1743 }
1744 let x = fl(vp.to_screen([i as f32 * st, 0.0])[0]);
1745 g.dc.fill(g.sf, x, y0, hair, y1 - y0);
1746 }
1747 for j in j0..=j1 {
1748 if (j.rem_euclid(5) == 0) != want {
1749 continue;
1750 }
1751 let y = fl(vp.to_screen([0.0, j as f32 * st])[1]);
1752 g.dc.fill(g.sf, x0, y, x1 - x0, hair);
1753 }
1754 }
1755}
1756
1757#[allow(clippy::too_many_arguments)]
1761pub fn dg_stroke_edge(g: &mut Gfx, vp: &Viewport, pts: &[[f32; 2]], width: f32, argb: u32, cap0: u32, cap1: u32, dash: u32) {
1762 if pts.len() < 2 {
1763 return;
1764 }
1765 let w = (width * vp.scale() as f32).max(1.0);
1766 let mut bb = [f32::MAX, f32::MAX, f32::MIN, f32::MIN];
1767 let path = &mut g.text.path;
1768 path.reset();
1769 path.set_origin(0, 0);
1770 for (i, p) in pts.iter().enumerate() {
1771 let q = vp.to_screen(*p);
1772 bb = [bb[0].min(q[0]), bb[1].min(q[1]), bb[2].max(q[0]), bb[3].max(q[1])];
1773 if i == 0 {
1774 path.move_f(q[0], q[1]);
1775 } else {
1776 path.line_f(q[0], q[1]);
1777 }
1778 }
1779 let m = w * 4.0 + 6.0; let c = g.dc.clip;
1781 if bb[2] + m < c[0] as f32 || bb[0] - m > c[2] as f32 || bb[3] + m < c[1] as f32 || bb[1] - m > c[3] as f32 {
1782 path.reset();
1783 return;
1784 }
1785 let st = DStyle { width: w as f64, pat: dash_pattern(dash), join: gfx::JOIN_ROUND, cap0, cap1, dcap: LCAP_FLAT, offset: 0.0, miter: 0.0 };
1786 dash_stroke(path, &st);
1787 g.dc.set_color(argb);
1788 path.fill(&mut g.text.ras, g.dc, g.sf, gfx::RULE_NONZERO);
1789}
1790
1791#[derive(Default)]
1795struct Backing {
1796 hash: u64,
1797 sf: Surface,
1798 content: u64,
1800 full: bool,
1801 view: CanvasView,
1802 rect: [i32; 4],
1803}
1804
1805pub fn dg_hairline(g: &mut Gfx, vp: &Viewport, pts: &[[f32; 2]], argb: u32) {
1809 if pts.len() < 2 || g.sf.fmt == gfx::FMT_A8 {
1810 return;
1811 }
1812 g.dc.set_color(argb);
1813 let mut a = vp.to_screen(pts[0]);
1814 for &q in &pts[1..] {
1815 let b = vp.to_screen(q);
1816 wu_line(g, a, b);
1817 a = b;
1818 }
1819}
1820
1821fn wu_line(g: &mut Gfx, a: [f32; 2], b: [f32; 2]) {
1824 let c = g.dc.clip;
1825 let (ox, oy) = (g.dc.ox as f32, g.dc.oy as f32);
1826 let (mut x0, mut y0, mut x1, mut y1) = (a[0] + ox, a[1] + oy, b[0] + ox, b[1] + oy);
1827 let lim = 1.0e6;
1828 if !(fabs(x0) < lim && fabs(y0) < lim && fabs(x1) < lim && fabs(y1) < lim) {
1829 return;
1830 }
1831 let steep = fabs(y1 - y0) > fabs(x1 - x0);
1832 if steep {
1833 core::mem::swap(&mut x0, &mut y0);
1834 core::mem::swap(&mut x1, &mut y1);
1835 }
1836 if x0 > x1 {
1837 core::mem::swap(&mut x0, &mut x1);
1838 core::mem::swap(&mut y0, &mut y1);
1839 }
1840 let (lo, hi, mlo, mhi) = if steep { (c[1], c[3], c[0], c[2]) } else { (c[0], c[2], c[1], c[3]) };
1842 let dx = x1 - x0;
1843 let grad = if dx > 1e-6 { (y1 - y0) / dx } else { 0.0 };
1844 let i0 = fl(x0 + 0.499).max(lo);
1846 let i1 = fl(x1 - 0.5).min(hi - 1);
1847 if i1 < i0 {
1848 return;
1849 }
1850 let mut y = ((y0 + grad * (i0 as f32 + 0.5 - x0) - 0.5) * 65536.0) as i64;
1852 let step = (grad * 65536.0) as i64;
1853 let (pcol, stride, w) = (g.dc.pcol, g.sf.stride as i64, g.sf.w);
1854 let px = &mut g.sf.px;
1855 for i in i0..=i1 {
1856 let j = (y >> 16) as i32;
1857 let f = ((y >> 8) & 0xFF) as u32;
1858 for (jj, cov) in [(j, 255 - f), (j + 1, f)] {
1859 if jj < mlo || jj >= mhi || cov == 0 {
1860 continue;
1861 }
1862 let (x, yy) = if steep { (jj, i) } else { (i, jj) };
1863 if x < 0 || x >= w || yy < 0 {
1864 continue;
1865 }
1866 let k = (yy as i64 * stride + x as i64 * 4) as usize;
1867 let Some(p) = px.get_mut(k..k + 4) else { continue };
1868 let ia = 255 - gfx::div255(pcol[3] * cov);
1869 for ch in 0..4 {
1870 p[ch] = (gfx::div255(pcol[ch] * cov) + gfx::div255(p[ch] as u32 * ia)).min(255) as u8;
1871 }
1872 }
1873 y += step;
1874 }
1875}
1876
1877struct Wrap {
1879 paint: Option<Rc<dyn DgPaint>>,
1880 vp: Viewport,
1881 geo: Rc<DgGeo>,
1882 theme: [u32; THEME_N],
1883 fonts: [(u32, i32); 7],
1884 grid: f32,
1885 dark: bool,
1886 moving: bool,
1887 direct: bool,
1889 hash: u64,
1890 content: u64,
1891 store: Rc<RefCell<Backing>>,
1892}
1893
1894impl Draw for Wrap {
1895 fn draw(&self, g: &mut Gfx, r: [i32; 4]) {
1896 let k = g.dc.clip;
1897 if k[2] <= k[0] || k[3] <= k[1] {
1898 return;
1899 }
1900 let v = self.vp.rect;
1901 let (w, h) = (v[0].max(0) + v[2], v[1].max(0) + v[3]);
1902 let Ok(mut b) = self.store.try_borrow_mut() else {
1903 self.paint_all(g, r);
1904 return;
1905 };
1906 let small = ((k[2] - k[0]) as i64 * (k[3] - k[1]) as i64) * 2 < v[2] as i64 * v[3] as i64;
1907 if self.direct && small && b.hash != self.hash {
1908 self.paint_all(g, r);
1909 return;
1910 }
1911 if b.hash != self.hash || b.sf.w != w || b.sf.h != h || b.sf.px.is_empty() {
1912 if b.sf.w != w || b.sf.h != h || b.sf.px.is_empty() {
1913 b.sf = Surface::new(w, h, FMT_XRGB);
1914 }
1915 if b.sf.px.is_empty() {
1916 b.hash = 0;
1918 drop(b);
1919 self.paint_all(g, r);
1920 return;
1921 }
1922 let Backing { hash, sf, content, full, view, rect } = &mut *b;
1923 g.dc.begin(sf);
1924 g.dc.clip_push(v[0], v[1], v[2], v[3]);
1925 self.paint_all(&mut Gfx { dc: &mut *g.dc, sf, text: &mut *g.text }, v);
1926 g.dc.end();
1927 *hash = self.hash;
1928 *content = self.content;
1929 *full = !self.moving;
1930 *view = view_of(&self.vp);
1931 *rect = v;
1932 }
1933 g.dc.blit(g.sf, &b.sf, r[0] - v[0], r[1] - v[1]);
1935 }
1936}
1937
1938fn view_of(vp: &Viewport) -> CanvasView {
1940 let s = vp.scale();
1941 let (mx, my) = vp.mid();
1942 CanvasView { scale: s, ox: mx - vp.cx * s, oy: my - vp.cy * s }
1943}
1944
1945impl CanvasContent for Wrap {
1946 fn draw_view(&self, g: &mut Gfx, t: CanvasView, rect: [i32; 4]) {
1949 let dpi = self.vp.dpi.max(1) as f64;
1950 let mut vp = self.vp;
1951 vp.rect = rect;
1952 vp.zoom = (t.scale * 96.0 / dpi) as f32;
1953 let (mx, my) = vp.mid();
1954 vp.cx = (mx - t.ox) / t.scale;
1955 vp.cy = (my - t.oy) / t.scale;
1956 let mut cut = None;
1957 if let Ok(b) = self.store.try_borrow() {
1958 let (dx, dy) = (b.view.ox - t.ox, b.view.oy - t.oy);
1959 let (rx, ry) = (fl((dx + 0.5) as f32) as f64, fl((dy + 0.5) as f32) as f64);
1960 let usable = b.full && b.content == self.content && !b.sf.px.is_empty() && b.view.scale == t.scale;
1961 if usable && (dx - rx).abs() < 1.0 / 64.0 && (dy - ry).abs() < 1.0 / 64.0 {
1962 let (rx, ry) = (rx as i32, ry as i32);
1963 let s = b.rect;
1964 let (x0, y0) = ((s[0] - rx).max(rect[0]), (s[1] - ry).max(rect[1]));
1965 let (x1, y1) = ((s[0] + s[2] - rx).min(rect[0] + rect[2]), (s[1] + s[3] - ry).min(rect[1] + rect[3]));
1966 if x0 < x1 && y0 < y1 {
1967 g.dc.clip_push(x0, y0, x1 - x0, y1 - y0);
1968 g.dc.blit(g.sf, &b.sf, -rx, -ry);
1969 g.dc.clip_pop();
1970 cut = Some([x0, y0, x1, y1]);
1971 }
1972 }
1973 }
1974 let Some([x0, y0, x1, y1]) = cut else {
1975 self.paint_view(g, &vp, rect, false);
1976 return;
1977 };
1978 let (rx1, ry1) = (rect[0] + rect[2], rect[1] + rect[3]);
1979 for s in [[rect[0], rect[1], rect[2], y0 - rect[1]], [rect[0], y1, rect[2], ry1 - y1], [rect[0], y0, x0 - rect[0], y1 - y0], [x1, y0, rx1 - x1, y1 - y0]] {
1980 if s[2] > 0 && s[3] > 0 {
1981 g.dc.clip_push(s[0], s[1], s[2], s[3]);
1982 self.paint_view(g, &vp, rect, false);
1983 g.dc.clip_pop();
1984 }
1985 }
1986 }
1987}
1988
1989impl Wrap {
1990 fn paint_all(&self, g: &mut Gfx, r: [i32; 4]) {
1992 self.paint_view(g, &self.vp, r, self.moving);
1993 }
1994
1995 fn paint_view(&self, g: &mut Gfx, vp: &Viewport, r: [i32; 4], moving: bool) {
1997 let k = g.dc.clip;
1998 let bg = canvas_bg(&self.theme);
1999 g.dc.set_color(bg);
2000 g.dc.fill(g.sf, r[0], r[1], r[2], r[3]);
2001 let a = vp.to_world(k[0] as f32, k[1] as f32);
2002 let b = vp.to_world(k[2] as f32, k[3] as f32);
2003 let f = DgFrame { vp: *vp, clip: [a[0], a[1], b[0], b[1]], geo: &self.geo, theme: &self.theme, bg, fonts: self.fonts, dark: self.dark, moving };
2004 if let Some(p) = &self.paint {
2006 p.paint_under(g, &f);
2007 }
2008 if self.grid > 0.0 {
2009 let div = self.theme[(T_DIVIDER / 4) as usize];
2010 let minor = over(bg, (div & 0x00FF_FFFF) | ((div >> 25) << 24));
2011 dg_paint_grid(g, vp, self.grid, minor, over(bg, div));
2012 }
2013 if let Some(p) = &self.paint {
2014 p.paint(g, &f);
2015 }
2016 }
2017}
2018
2019struct Mini {
2022 rects: Vec<([i32; 4], bool, bool)>,
2023 node: u32,
2024 sel: u32,
2025}
2026
2027impl Draw for Mini {
2028 fn draw(&self, g: &mut Gfx, _r: [i32; 4]) {
2029 for (r, s, outline) in &self.rects {
2030 g.dc.set_color(if *s { self.sel } else { self.node });
2031 if *outline {
2032 g.dc.frame(g.sf, r[0], r[1], r[2], r[3]);
2033 } else {
2034 g.dc.fill(g.sf, r[0], r[1], r[2], r[3]);
2035 }
2036 }
2037 }
2038}
2039
2040fn canvas_bg(theme: &[u32]) -> u32 {
2041 theme[(T_BG / 4) as usize] | 0xFF00_0000
2042}
2043
2044fn over(bg: u32, c: u32) -> u32 {
2046 color_mix(bg | 0xFF00_0000, c | 0xFF00_0000, ((c >> 24) * 256 / 255) as i32)
2047}
2048
2049struct Fnv(u64);
2050
2051impl Fnv {
2052 fn new() -> Fnv {
2053 Fnv(0xCBF2_9CE4_8422_2325)
2054 }
2055 fn u64(&mut self, v: u64) {
2056 for b in v.to_le_bytes() {
2057 self.0 ^= b as u64;
2058 self.0 = self.0.wrapping_mul(0x0000_0100_0000_01B3);
2059 }
2060 }
2061}
2062
2063fn fabs(x: f32) -> f32 {
2066 if x < 0.0 { -x } else { x }
2067}
2068
2069fn fl(x: f32) -> i32 {
2071 if x.is_finite() { floor_i(x.clamp(-1.0e9, 1.0e9)) } else { 0 }
2072}
2073
2074fn snapv(v: f32, g: f32) -> f32 {
2075 fl(v / g + 0.5) as f32 * g
2076}
2077
2078fn dist(a: [f32; 2], b: [f32; 2]) -> f32 {
2079 let (dx, dy) = (a[0] - b[0], a[1] - b[1]);
2080 sqrt(dx * dx + dy * dy)
2081}
2082
2083fn xyxy(r: [f32; 4]) -> [f32; 4] {
2085 [r[0], r[1], r[0] + r[2], r[1] + r[3]]
2086}
2087
2088fn overlaps(a: [f32; 4], b: [f32; 4]) -> bool {
2089 a[0] <= b[2] && b[0] <= a[2] && a[1] <= b[3] && b[1] <= a[3]
2090}
2091
2092fn inside(a: [f32; 4], b: [f32; 4]) -> bool {
2094 a[0] >= b[0] && a[1] >= b[1] && a[2] <= b[2] && a[3] <= b[3]
2095}
2096
2097fn union(a: Option<[f32; 4]>, b: [f32; 4]) -> Option<[f32; 4]> {
2098 Some(match a {
2099 None => b,
2100 Some(a) => [a[0].min(b[0]), a[1].min(b[1]), a[2].max(b[2]), a[3].max(b[3])],
2101 })
2102}
2103
2104fn range_of(r: &Range<u32>, len: usize) -> Range<usize> {
2105 let a = (r.start as usize).min(len);
2106 let b = (r.end as usize).min(len).max(a);
2107 a..b
2108}
2109
2110fn ipx(r: [f32; 4]) -> [i32; 4] {
2112 let (x0, y0) = (fl(r[0] + 0.5), fl(r[1] + 0.5));
2113 let (x1, y1) = (fl(r[0] + r[2] + 0.5), fl(r[1] + r[3] + 0.5));
2114 [x0, y0, (x1 - x0).max(1), (y1 - y0).max(1)]
2115}
2116
2117fn fx(v: f32) -> i32 {
2119 (v.clamp(-4.0e6, 4.0e6) * 256.0) as i32
2120}
2121
2122fn node_sem_id(id: u32, key: u64) -> u32 {
2124 let mut h = Fnv::new();
2125 h.u64(id as u64);
2126 h.u64(key);
2127 0x5000_0000 | (h.0 as u32 ^ (h.0 >> 32) as u32) & 0x0FFF_FFFF
2128}
2129
2130impl Ui {
2133 #[cfg_attr(jfgui_dev, track_caller)]
2136 pub fn diagram(&mut self, id: u32, st: &mut DiagramState, m: &DModel, tool: u32, paint: Option<Rc<dyn DgPaint>>, ver: u32) -> Vec<DEv> {
2137 let _src = self.src_scope();
2138 let mut out = Vec::new();
2139 let _ = self.take_flags();
2140 let r = self.place(0, 0);
2141 let dpi = self.wp.rt.dpi;
2142 st.id = id;
2143 st.vp.rect = r;
2144 st.vp.dpi = dpi;
2145 st.vp.zoom = clampz(st.vp.zoom);
2146 if st.ix.update(m) && st.prune() {
2147 out.push(DEv::Select);
2148 }
2149 if st.fit_req && r[2] > 0 && r[3] > 0 {
2150 st.fit_req = false;
2151 match st.ix.bounds {
2152 Some(b) => st.vp.fit(b, 24),
2153 None => {
2154 st.vp.cx = 0.0;
2155 st.vp.cy = 0.0;
2156 }
2157 }
2158 }
2159 if st.pending_sel {
2160 st.pending_sel = false;
2161 out.push(DEv::Select);
2162 }
2163 let view0 = (st.vp.zoom, st.vp.cx, st.vp.cy);
2164 let c = &self.wp.ctx;
2166 let (hot, mine0) = (c.hot == id, c.active == id);
2167 let (mx, my) = (self.rtl_map(c.mx, r[0], r[2]), c.my);
2168 let inside_r = c.mx != -1 && mx >= r[0] && my >= r[1] && mx < r[0] + r[2] && my < r[1] + r[3];
2169 let mut mods = self.mods;
2170 if st.test_keys & 2 != 0 {
2171 mods |= MOD_CTRL;
2172 }
2173 let space = st.test_keys & 1 != 0 || self.key_down(VK_SPACE);
2174 let p = st.vp.to_world(mx as f32 + 0.5, my as f32 + 0.5);
2175 let track = (hot || mine0) && inside_r && !space && !matches!(st.g, Gesture::Pan { .. });
2176 st.hover = if track { st.hit(m, p, tool, dpi) } else { DHit::None };
2177 let cur = st.cursor(m, tool, space);
2178 let enter = self.wp.ctx.focus == id && self.key_peek(VK_RETURN);
2180 let lim = self.dp(DRAG_DIP);
2181 let notch = self.dp(48);
2182 let ev = self.behave(id, HB_FOCUS | HB_MOVES | HB_GRAB_H | HB_GRAB_V | cur << HB_CURSOR_SHIFT);
2183 let state = self.wp.ctx.last_state;
2184 let c = &self.wp.ctx;
2185 let (now, dbl) = (self.now_ms, self.dbl_ms);
2186 let inp = In { tool, mods, space, ev, px: [mx, my], p, btn: c.mbtn, pressed: c.pressed, mine: c.active == id, lim, now, dbl };
2187 if ev & EV_HOVER != 0 && (c.wheel_x != 0 || c.wheel_y != 0) {
2188 let wx = if self.rtl { -c.wheel_x } else { c.wheel_x };
2189 st.wheel(wx, c.wheel_y, mods, [mx, my], notch);
2190 st.moving_until = now.wrapping_add(MOVING_MS);
2191 }
2192 st.step(m, &inp, &mut out);
2193 if ev & EV_RCLICK != 0 {
2194 let at = [self.wp.ctx.mx, my];
2195 st.context(st.hover, at, &mut out);
2196 }
2197 if self.wp.ctx.focus == id {
2198 self.dg_keys(st, m, enter, &mut out);
2199 }
2200 if (st.vp.zoom, st.vp.cx, st.vp.cy) != view0 {
2201 out.push(DEv::ViewChanged);
2202 }
2203 if !out.is_empty() {
2204 let p = st.vp.to_world(mx as f32 + 0.5, my as f32 + 0.5);
2206 st.hover = if track { st.hit(m, p, tool, dpi) } else { DHit::None };
2207 self.repass();
2208 }
2209 self.ltr_begin(r[0], r[2]);
2211 self.clip_push(r[0], r[1], r[2], r[3]);
2212 let wheel = (st.moving_until.wrapping_sub(self.now_ms) as i32) > 0;
2216 if wheel {
2217 self.animating = true;
2218 }
2219 let view_moves = wheel || matches!(st.g, Gesture::Pan { .. });
2220 let moving = view_moves || matches!(st.g, Gesture::Move { .. } | Gesture::Waypoint { .. } | Gesture::Resize { .. });
2221 let (h, obj, content) = self.dg_paint_obj(st, m, paint, ver, moving, view_moves);
2222 self.cmd_custom(r[0], r[1], r[2], r[3], Some(obj.clone()), 0, h as u32);
2223 let view = view_of(&st.vp);
2224 let gpu = st.gpu;
2225 self.canvas_gpu(CanvasDesc { id, name: "diagram", rect: r, view, ver: (content ^ content >> 32) as u32, content: obj, gpu, exact_pan: false });
2226 let ring = self.wp.ctx.focus == id && self.wp.ctx.focus_visible;
2227 self.dg_overlay(st, m, tool, ring);
2228 self.clip_pop();
2229 self.ltr_end();
2230 self.wp.ctx.last_rect = r;
2231 self.wp.ctx.last_state = state;
2232 let name = if st.name.is_empty() { None } else { Some(st.name.clone()) };
2233 self.sem(K_CANVAS, id, name.as_deref(), st.sel.len() as i32);
2234 out
2235 }
2236
2237 fn dg_key_find(&self, vk: u32, need: u32, mask: u32) -> Option<(usize, u32)> {
2240 for (i, &k) in self.wp.ctx.keyq.iter().enumerate() {
2241 if (k as i32) < 0 {
2242 continue;
2243 }
2244 let mods = (k >> 16) & 0x7FFF;
2245 let mut v = k & 0xFFFF;
2246 if self.rtl && mods & (MOD_CTRL | MOD_ALT) == 0 {
2247 v = match v {
2248 VK_LEFT => VK_RIGHT,
2249 VK_RIGHT => VK_LEFT,
2250 v => v,
2251 };
2252 }
2253 if v == vk && mods & mask == need {
2254 return Some((i, mods));
2255 }
2256 }
2257 None
2258 }
2259
2260 fn dg_key_take(&mut self, i: usize) {
2261 if let Some(k) = self.wp.ctx.keyq.get_mut(i) {
2262 *k |= 0x8000_0000;
2263 }
2264 }
2265
2266 fn dg_keys(&mut self, st: &mut DiagramState, m: &DModel, enter: bool, out: &mut Vec<DEv>) {
2268 if st.last_hit == Some(DHit::None) && !self.wp.ctx.keyq.is_empty() {
2270 st.last_hit = None;
2271 }
2272 let r = st.vp.rect;
2273 let idle = matches!(st.g, Gesture::Idle);
2274 if let Some((i, _)) = self.dg_key_find(VK_ESCAPE, 0, 0) {
2276 let done = st.cancel(out) || {
2277 let any = !st.sel.is_empty();
2278 if any {
2279 st.sel.clear();
2280 out.push(DEv::Select);
2281 }
2282 any
2283 };
2284 if done {
2285 self.dg_key_take(i);
2286 }
2287 }
2288 if !idle {
2289 return;
2290 }
2291 if let Some((i, _)) = self.dg_key_find(VK_DELETE, 0, MOD_CTRL | MOD_ALT) {
2292 if !st.sel.is_empty() {
2293 self.dg_key_take(i);
2294 out.push(DEv::Delete);
2295 }
2296 }
2297 if let Some((i, _)) = self.dg_key_find(VK_A, MOD_CTRL, MOD_CTRL | MOD_ALT) {
2298 self.dg_key_take(i);
2299 let mut all: BTreeSet<u64> = st.walk_keys(m).into_iter().collect();
2300 all.extend(st.ix.geo.routes.iter().filter(|rt| rt.pts.len() >= 2).map(|rt| rt.key));
2301 if all != st.sel {
2302 st.sel = all;
2303 out.push(DEv::Select);
2304 }
2305 }
2306 for (vk, dx, dy) in [(VK_LEFT, -1.0f32, 0.0f32), (VK_RIGHT, 1.0, 0.0), (VK_UP, 0.0, -1.0), (VK_DOWN, 0.0, 1.0)] {
2308 let Some((i, km)) = self.dg_key_find(vk, 0, MOD_CTRL) else { continue };
2309 self.dg_key_take(i);
2310 let keys = st.movable_sel(m);
2311 if keys.is_empty() {
2312 let s = st.vp.scale();
2313 st.vp.cx += dx as f64 * r[2] as f64 / 8.0 / s;
2314 st.vp.cy += dy as f64 * r[3] as f64 / 8.0 / s;
2315 continue;
2316 }
2317 let base = if st.grid > 0.0 { st.grid } else { 8.0 };
2318 let k = if km & MOD_ALT != 0 {
2319 1.0
2320 } else if km & MOD_SHIFT != 0 {
2321 base * 10.0
2322 } else {
2323 base
2324 };
2325 let d = [dx * k, dy * k];
2326 out.push(DEv::Move { keys, d, total: d, done: true });
2327 }
2328 if let Some((i, km)) = self.dg_key_find(VK_TAB, 0, MOD_CTRL | MOD_ALT) {
2331 let keys = st.tab_keys(m);
2332 let pos = keys.iter().position(|&k| k == st.focus);
2333 let next = match (pos, km & MOD_SHIFT != 0) {
2334 (None, false) => keys.first().copied(),
2335 (None, true) => keys.last().copied(),
2336 (Some(p), false) => keys.get(p + 1).copied(),
2337 (Some(p), true) => p.checked_sub(1).and_then(|p| keys.get(p).copied()),
2338 };
2339 if let Some(k) = next {
2340 self.dg_key_take(i);
2341 self.wp.ctx.focus_visible = true;
2342 st.focus = k;
2343 if st.sel.len() != 1 || !st.sel.contains(&k) {
2344 st.sel.clear();
2345 st.sel.insert(k);
2346 out.push(DEv::Select);
2347 }
2348 st.reveal(m, k);
2349 }
2350 }
2351 let subject = if st.focus != 0 && st.ix.nodes.contains_key(&st.focus) {
2353 DHit::Node(st.focus)
2354 } else if st.focus != 0 && st.ix.edges.contains_key(&st.focus) && st.sel.contains(&st.focus) {
2355 DHit::Edge { key: st.focus, seg: 0, t: 0.5 }
2356 } else if st.sel.len() == 1 {
2357 let k = *st.sel.iter().next().unwrap_or(&0);
2358 if st.ix.nodes.contains_key(&k) { DHit::Node(k) } else { DHit::Edge { key: k, seg: 0, t: 0.5 } }
2359 } else {
2360 DHit::None
2361 };
2362 if enter && subject != DHit::None {
2363 out.push(DEv::Activate(subject));
2364 }
2365 let ctx = self.dg_key_find(VK_APPS, 0, 0).or_else(|| self.dg_key_find(VK_F10, MOD_SHIFT, MOD_SHIFT | MOD_CTRL | MOD_ALT));
2366 if let Some((i, _)) = ctx {
2367 self.dg_key_take(i);
2368 let at = match subject {
2369 DHit::Node(k) => st.node(m, k).map(|n| st.vp.to_screen([n.rect[0] + n.rect[2] * 0.5, n.rect[1] + n.rect[3] * 0.5])),
2370 DHit::Edge { key, .. } => st.route(key).map(|rt| st.vp.to_screen(dg_point_at(&rt.pts, 0.5))),
2371 _ => None,
2372 };
2373 let at = at.map_or([r[0] + r[2] / 2, r[1] + r[3] / 2], |q| [fl(q[0]), fl(q[1])]);
2374 let at = [self.rtl_map(at[0], r[0], r[2]), at[1]];
2375 st.context(subject, at, out);
2376 }
2377 let (cx, cy) = (r[0] as f32 + r[2] as f32 * 0.5, r[1] as f32 + r[3] as f32 * 0.5);
2379 for (vk, dir) in [(VK_OEM_PLUS, 1), (VK_ADD, 1), (VK_OEM_MINUS, -1), (VK_SUBTRACT, -1)] {
2380 if let Some((i, _)) = self.dg_key_find(vk, MOD_CTRL, MOD_CTRL | MOD_ALT) {
2381 self.dg_key_take(i);
2382 st.vp.step(dir, cx, cy);
2383 }
2384 }
2385 for vk in [VK_0, VK_NUMPAD0] {
2386 if let Some((i, _)) = self.dg_key_find(vk, MOD_CTRL, MOD_CTRL | MOD_ALT) {
2387 self.dg_key_take(i);
2388 if let Some(b) = st.ix.bounds {
2389 st.vp.fit(b, 24);
2390 }
2391 }
2392 }
2393 }
2394
2395 fn dg_paint_obj(&mut self, st: &mut DiagramState, m: &DModel, paint: Option<Rc<dyn DgPaint>>, ver: u32, direct: bool, view_moves: bool) -> (u64, Rc<Wrap>, u64) {
2397 let mut fonts = [(0u32, 0i32); 7];
2398 for (ts, f) in fonts.iter_mut().enumerate() {
2399 self.ts_font(ts as u32);
2400 *f = self.text.get_font();
2401 }
2402 let vp = st.vp;
2403 let mut h = Fnv::new();
2405 h.u64(ver as u64 | (m.ver as u64) << 32);
2406 h.u64(Rc::as_ptr(&st.ix.geo) as usize as u64);
2407 h.u64(paint.as_ref().map_or(0, |p| Rc::as_ptr(p) as *const () as usize as u64));
2408 h.u64(vp.dpi as u64);
2409 h.u64(st.grid.to_bits() as u64 | (self.theme_dark as u64) << 32);
2410 for &c in self.theme.iter() {
2411 h.u64(c as u64);
2412 }
2413 for &(f, s) in &fonts {
2414 h.u64(f as u64 | (s as u32 as u64) << 32);
2415 }
2416 let content = h.0;
2417 let moving = direct && !(view_moves && st.gpu != CanvasGpu::Cpu && self.gp_canvas_steady(st.id, (content ^ content >> 32) as u32));
2420 h.u64(vp.zoom.to_bits() as u64);
2421 h.u64(vp.cx.to_bits());
2422 h.u64(vp.cy.to_bits());
2423 h.u64(vp.rect[0] as u32 as u64 | (vp.rect[1] as u32 as u64) << 32);
2424 h.u64(vp.rect[2] as u32 as u64 | (vp.rect[3] as u32 as u64) << 32);
2425 h.u64(moving as u64);
2426 let hash = h.0;
2427 if let Some((k, d)) = &st.paint_obj {
2428 if *k == hash {
2429 return (hash, d.clone(), content);
2430 }
2431 }
2432 let d = Rc::new(Wrap { paint, vp, geo: st.ix.geo.clone(), theme: self.theme, fonts, grid: st.grid, dark: self.theme_dark, moving, direct, hash, content, store: st.store.clone() });
2433 st.paint_obj = Some((hash, d.clone()));
2434 (hash, d, content)
2435 }
2436
2437 fn dg_poly(&mut self, vp: &Viewport, pts: &[[f32; 2]], w: f32, c: u32) {
2439 if pts.len() < 2 {
2440 return;
2441 }
2442 self.draw_color(c);
2443 let wfx = fx(w);
2444 let r = vp.rect;
2445 let g = w + 2.0;
2446 let (x0, y0, x1, y1) = (r[0] as f32 - g, r[1] as f32 - g, (r[0] + r[2]) as f32 + g, (r[1] + r[3]) as f32 + g);
2447 let mut a = vp.to_screen(pts[0]);
2448 for &q in &pts[1..] {
2449 let b = vp.to_screen(q);
2450 let off = (a[0] < x0 && b[0] < x0) || (a[0] > x1 && b[0] > x1) || (a[1] < y0 && b[1] < y0) || (a[1] > y1 && b[1] > y1);
2451 if !off {
2452 self.draw_line(fx(a[0]), fx(a[1]), fx(b[0]), fx(b[1]), wfx);
2453 }
2454 a = b;
2455 }
2456 }
2457
2458 fn dg_dashed(&mut self, a: [f32; 2], b: [f32; 2], w: f32, c: u32) {
2460 let len = dist(a, b);
2461 if !(len > 0.5) {
2462 return;
2463 }
2464 self.draw_color(c);
2465 let (on, off) = (self.dp(6) as f32, self.dp(4) as f32);
2466 let n = ((len / (on + off)) as i32 + 1).min(400);
2467 for k in 0..n {
2468 let t0 = k as f32 * (on + off) / len;
2469 if t0 >= 1.0 {
2470 break;
2471 }
2472 let t1 = ((k as f32 * (on + off) + on) / len).min(1.0);
2473 let p = [a[0] + (b[0] - a[0]) * t0, a[1] + (b[1] - a[1]) * t0];
2474 let q = [a[0] + (b[0] - a[0]) * t1, a[1] + (b[1] - a[1]) * t1];
2475 self.draw_line(fx(p[0]), fx(p[1]), fx(q[0]), fx(q[1]), fx(w));
2476 }
2477 }
2478
2479 fn dg_dot(&mut self, p: [f32; 2], r: i32, t: i32, fill: u32, stroke: u32) {
2481 let (x, y) = (fl(p[0] + 0.5) - r, fl(p[1] + 0.5) - r);
2482 self.cmd_rrect(x, y, r * 2, r * 2, r, t, fill, stroke, stroke);
2483 }
2484
2485 fn dg_overlay(&mut self, st: &mut DiagramState, m: &DModel, tool: u32, ring: bool) {
2487 let vp = st.vp;
2488 let s = vp.scale() as f32;
2489 let wr = vp.world_rect();
2490 let accent = self.color(T_ACCENT);
2491 let bg = self.color(T_BG) | 0xFF00_0000;
2492 let hair = self.hair();
2493 let mut vis = core::mem::take(&mut st.visible);
2495 st.ix.query(wr, false, m.nodes.len(), &mut vis);
2496 vis.retain(|&i| m.nodes.get(i as usize).is_some_and(|n| overlaps(xyxy(n.rect), wr)));
2497 let ew = (st.edge_width * s).max(1.0);
2499 let hov_edge = if let DHit::Edge { key, .. } = st.hover { key } else { 0 };
2500 let mut sel_edges = Vec::new();
2501 for &k in &st.sel {
2502 if let Some(&i) = st.ix.edges.get(&k) {
2503 sel_edges.push(i as usize);
2504 }
2505 }
2506 let geo = st.ix.geo.clone();
2507 let mut edge_ring = false;
2510 if ring && st.focus != 0 {
2511 if let Some(rt) = st.ix.edges.get(&st.focus).and_then(|&i| geo.routes.get(i as usize)) {
2512 if overlaps(rt.bb, wr) {
2513 let (fo, fi) = (self.color(T_FOCUS_OUT), self.color(T_FOCUS_IN));
2514 self.dg_poly(&vp, &rt.pts, ew + self.dp(8) as f32, fo);
2515 self.dg_poly(&vp, &rt.pts, ew + self.dp(5) as f32, fi);
2516 edge_ring = true;
2517 }
2518 }
2519 }
2520 for &i in &sel_edges {
2521 if let Some(rt) = geo.routes.get(i) {
2522 if overlaps(rt.bb, wr) {
2523 self.dg_poly(&vp, &rt.pts, ew + self.dp(2) as f32, accent);
2524 }
2525 }
2526 }
2527 if hov_edge != 0 && !st.sel.contains(&hov_edge) && matches!(st.g, Gesture::Idle) {
2528 if let Some(rt) = st.route(hov_edge) {
2529 let c = self.color(T_ACCENT_HOVER);
2530 let pts = rt.pts.clone();
2531 self.dg_poly(&vp, &pts, ew, c);
2532 }
2533 }
2534 let hov_node = match st.hover {
2536 DHit::Node(k) | DHit::Toggle(k) => k,
2537 DHit::Port(k) => st.node_index(k).and_then(|i| m.nodes.get(i as usize)).map_or(0, |n| n.key),
2538 _ => 0,
2539 };
2540 let (pad, t2, rad) = (self.dp(3), self.dp(2), self.dp(4));
2541 let (fo, fi) = (self.color(T_FOCUS_OUT), self.color(T_FOCUS_IN));
2542 for &i in &vis {
2543 let n = &m.nodes[i as usize];
2544 let q = ipx(vp.rect_to_screen(n.rect));
2545 if st.sel.contains(&n.key) {
2546 self.cmd_rrect(q[0] - pad, q[1] - pad, q[2] + pad * 2, q[3] + pad * 2, rad, t2, 0, accent, accent);
2547 } else if n.key == hov_node && matches!(st.g, Gesture::Idle | Gesture::Press { .. }) {
2548 let c = (accent & 0x00FF_FFFF) | 0x9000_0000;
2549 self.cmd_rrect(q[0] - pad, q[1] - pad, q[2] + pad * 2, q[3] + pad * 2, rad, hair, 0, c, c);
2550 }
2551 if ring && st.focus == n.key {
2552 let g = pad + t2 + self.dp(1);
2553 self.cmd_rrect(q[0] - g, q[1] - g, q[2] + g * 2, q[3] + g * 2, rad + t2, t2, 0, fo, fo);
2554 self.cmd_rrect(q[0] - g + t2, q[1] - g + t2, q[2] + (g - t2) * 2, q[3] + (g - t2) * 2, rad, hair, 0, fi, fi);
2555 }
2556 if st.hover == DHit::Toggle(n.key) && matches!(st.g, Gesture::Idle | Gesture::Press { .. }) {
2558 let b = ipx(vp.rect_to_screen(dg_toggle_rect(n, st.frame_bar)));
2559 let c = self.color(T_SUBTLE_HOVER);
2560 self.cmd_rrect(b[0] + hair, b[1] + hair, b[2] - hair * 2, b[3] - hair * 2, self.dp(3), hair, c, accent, accent);
2561 }
2562 if n.flags & DN_RESIZE != 0 && tool == DG_SELECT && st.sel.contains(&n.key) {
2564 let h = if st.hover == DHit::Resize(n.key) { self.dp(5) + hair } else { self.dp(5) };
2565 let (x, y) = (q[0] + q[2] - h, q[1] + q[3] - h);
2566 self.cmd_rrect(x, y, h * 2, h * 2, self.dp(2), hair, accent, bg, bg);
2567 }
2568 }
2569 if ring && !edge_ring && !vis.iter().any(|&i| m.nodes[i as usize].key == st.focus) {
2571 let r = vp.rect;
2572 self.cmd_rrect(r[0], r[1], r[2], r[3], 0, t2, 0, fo, fo);
2573 }
2574 if tool == DG_SELECT {
2576 let (hr, mr) = (self.dp(5), self.dp(4));
2577 for &i in &sel_edges {
2578 let (Some(e), Some(rt)) = (m.edges.get(i), geo.routes.get(i)) else { continue };
2579 if !overlaps(rt.bb, wr) {
2580 continue;
2581 }
2582 for j in 0..rt.marks.len().saturating_sub(1) {
2583 if let Some(q) = mid_of(rt, j) {
2584 let hot = st.hover == DHit::Mid(e.key, j as u32);
2585 self.dg_dot(vp.to_screen(q), if hot { mr + hair } else { mr }, hair.max(self.dp(1)), bg, accent);
2586 }
2587 }
2588 for (j, q) in m.wps[range_of(&e.wps, m.wps.len())].iter().enumerate() {
2589 let hot = st.hover == DHit::Waypoint(e.key, j as u32);
2590 let p = vp.to_screen(*q);
2591 let h = if hot { hr + hair } else { hr };
2592 let (x, y) = (fl(p[0] + 0.5) - h, fl(p[1] + 0.5) - h);
2593 self.cmd_rrect(x, y, h * 2, h * 2, self.dp(2), hair, accent, bg, bg);
2594 }
2595 }
2596 }
2597 let mut port_nodes = Vec::new();
2599 let (mut hot_port, mut src_port) = (match st.hover {
2600 DHit::Port(k) => k,
2601 _ => 0,
2602 }, 0);
2603 if tool != DG_PAN && tool != DG_PLACE && hov_node != 0 {
2604 port_nodes.push(hov_node);
2605 }
2606 if let Gesture::Connect { from, over, .. } = st.g {
2607 src_port = from;
2608 hot_port = over;
2609 for k in [from, over] {
2610 if let Some(n) = st.node_index(k).and_then(|i| m.nodes.get(i as usize)) {
2611 port_nodes.push(n.key);
2612 }
2613 }
2614 }
2615 port_nodes.sort_unstable();
2616 port_nodes.dedup();
2617 let (pr, pr2) = (self.dp(4), self.dp(6));
2618 let strong = self.color(T_STRONG_STROKE);
2619 for k in port_nodes {
2620 let Some(n) = st.node(m, k) else { continue };
2621 for q in &m.ports[range_of(&n.ports, m.ports.len())] {
2622 if q.flags & DPT_HIDDEN != 0 {
2623 continue;
2624 }
2625 let p = vp.to_screen([n.rect[0] + q.at[0], n.rect[1] + q.at[1]]);
2626 if q.key == hot_port || q.key == src_port {
2627 self.dg_dot(p, pr2, t2, if q.flags & DPT_USED != 0 { accent } else { bg }, accent);
2628 } else {
2629 self.dg_dot(p, pr, hair.max(self.dp(1)), if q.flags & DPT_USED != 0 { accent } else { bg }, strong);
2630 }
2631 }
2632 }
2633 if let Gesture::Marquee { start, cur, .. } = &st.g {
2635 let a = vp.to_screen(*start);
2636 let b = vp.to_screen(*cur);
2637 let q = ipx([a[0].min(b[0]), a[1].min(b[1]), fabs(b[0] - a[0]), fabs(b[1] - a[1])]);
2638 let fill = (accent & 0x00FF_FFFF) | 0x2400_0000;
2639 self.cmd_rrect(q[0], q[1], q[2], q[3], 0, hair, fill, accent, accent);
2640 }
2641 if let Gesture::Connect { from, at, over, .. } = st.g {
2643 let end = if over != 0 { ep_of(m, Some(&st.ix), over).map(|e| anchor(e, at, false).0).unwrap_or(at) } else { at };
2644 if let Some(e) = ep_of(m, Some(&st.ix), from) {
2645 let a = vp.to_screen(anchor(e, end, false).0);
2646 let b = vp.to_screen(end);
2647 self.dg_dashed(a, b, self.dp(2) as f32, accent);
2648 self.dg_dot(b, self.dp(3), 0, accent, 0);
2649 }
2650 }
2651 st.visible = vis;
2652 }
2653
2654 #[cfg_attr(jfgui_dev, track_caller)]
2658 pub fn diagram_minimap(&mut self, id: u32, st: &mut DiagramState, m: &DModel) -> bool {
2659 let _src = self.src_scope();
2660 let r = st.vp.rect;
2661 let (w, h, mg) = (self.dp(176), self.dp(112), self.dp(12));
2662 if r[2] < w + mg * 2 + self.dp(160) || r[3] < h + mg * 2 + self.dp(120) {
2663 return false;
2664 }
2665 let mr = [r[0] + r[2] - w - mg, r[1] + r[3] - h - mg, w, h];
2666 let pad = self.dp(6);
2667 let inner = [mr[0] + pad, mr[1] + pad, mr[2] - pad * 2, mr[3] - pad * 2];
2668 let held = self.wp.ctx.active == id;
2670 let ext = match (held, st.mini_ext) {
2671 (true, Some(e)) => e,
2672 _ => {
2673 let v = st.vp.world_rect();
2674 let b = st.ix.bounds.map_or(v, |b| [b[0].min(v[0]), b[1].min(v[1]), b[2].max(v[2]), b[3].max(v[3])]);
2675 let (gx, gy) = ((b[2] - b[0]) * 0.04 + 1.0, (b[3] - b[1]) * 0.04 + 1.0);
2676 let e = [b[0] - gx, b[1] - gy, b[2] + gx, b[3] + gy];
2677 st.mini_ext = Some(e);
2678 e
2679 }
2680 };
2681 let (ew, eh) = (ext[2] - ext[0], ext[3] - ext[1]);
2682 let k = (inner[2] as f32 / ew).min(inner[3] as f32 / eh);
2683 let ox = inner[0] as f32 + (inner[2] as f32 - ew * k) * 0.5;
2684 let oy = inner[1] as f32 + (inner[3] as f32 - eh * k) * 0.5;
2685 let map = |p: [f32; 2]| [ox + (p[0] - ext[0]) * k, oy + (p[1] - ext[1]) * k];
2686 let hx = if self.rtl { self.rtl_map(mr[0] + mr[2] - 1, r[0], r[2]) } else { mr[0] };
2688 let ev = self.hit_rect(id, hx, mr[1], mr[2], mr[3], HB_MOVES | CUR_HAND << HB_CURSOR_SHIFT);
2689 let state = self.wp.ctx.last_state;
2690 let mut moved = false;
2691 if ev & EV_HELD != 0 && k > 0.0 {
2692 let c = &self.wp.ctx;
2693 let (mx, my) = (self.rtl_map(c.mx, r[0], r[2]), c.my);
2694 let wx = ext[0] as f64 + (mx as f64 + 0.5 - ox as f64) / k as f64;
2695 let wy = ext[1] as f64 + (my as f64 + 0.5 - oy as f64) / k as f64;
2696 if wx != st.vp.cx || wy != st.vp.cy {
2697 st.vp.cx = wx;
2698 st.vp.cy = wy;
2699 moved = true;
2700 st.moving_until = self.now_ms.wrapping_add(MOVING_MS);
2701 self.repass();
2702 }
2703 }
2704 let fill = over(self.color(T_BG), self.color(T_FLYOUT));
2706 let stroke = self.color(T_FLYOUT_STROKE);
2707 let (node_c, sel_c) = (over(fill, self.color(T_TEXT3)), self.color(T_ACCENT));
2708 self.ltr_begin(r[0], r[2]);
2709 self.cmd_rrect(mr[0], mr[1], mr[2], mr[3], self.dp(6), self.hair(), fill, stroke, stroke);
2710 let mut hh = Fnv::new();
2711 hh.u64(m.ver as u64 | (node_c as u64) << 32);
2712 hh.u64(sel_c as u64 | (mr[0] as u32 as u64) << 32);
2713 hh.u64(mr[1] as u32 as u64 | (mr[2] as u32 as u64) << 32);
2714 for v in ext {
2715 hh.u64(v.to_bits() as u64);
2716 }
2717 for &s in &st.sel {
2718 hh.u64(s);
2719 }
2720 let hash = hh.0;
2721 let obj = match &st.mini_obj {
2722 Some((h, d)) if *h == hash => d.clone(),
2723 _ => {
2724 let mut rects = Vec::with_capacity(m.nodes.len());
2725 for n in m.nodes {
2726 let a = map([n.rect[0], n.rect[1]]);
2727 let q = [fl(a[0]), fl(a[1]), fl(n.rect[2] * k).max(1), fl(n.rect[3] * k).max(1)];
2728 rects.push((q, st.sel.contains(&n.key), n.flags & (DN_FRAME | DN_OUTLINE) != 0));
2729 }
2730 let d: DrawRef = Rc::new(Mini { rects, node: node_c, sel: sel_c });
2731 st.mini_obj = Some((hash, d.clone()));
2732 d
2733 }
2734 };
2735 self.clip_push(inner[0], inner[1], inner[2], inner[3]);
2736 self.cmd_custom(inner[0], inner[1], inner[2], inner[3], Some(obj), 0, hash as u32);
2737 let v = st.vp.world_rect();
2738 let (a, b) = (map([v[0], v[1]]), map([v[2], v[3]]));
2739 let q = ipx([a[0], a[1], b[0] - a[0], b[1] - a[1]]);
2740 self.draw_color(sel_c);
2741 self.draw_frame(q[0], q[1], q[2], q[3], 0, self.dp(1).max(1));
2742 self.clip_pop();
2743 self.ltr_end();
2744 self.wp.ctx.last_rect = [hx, mr[1], mr[2], mr[3]];
2745 self.wp.ctx.last_state = state;
2746 self.sem(K_CANVAS, id, None, 0);
2747 moved
2748 }
2749
2750 #[cfg_attr(jfgui_dev, track_caller)]
2754 pub fn diagram_sems(&mut self, id: u32, st: &mut DiagramState, m: &DModel, label: &dyn Fn(u64) -> String) {
2755 let _src = self.src_scope();
2756 let r = st.vp.rect;
2757 let focused = self.wp.ctx.focus == id;
2758 let hov = match st.hover {
2759 DHit::Node(k) | DHit::Toggle(k) => k,
2760 _ => 0,
2761 };
2762 let vis = core::mem::take(&mut st.visible);
2763 for &i in &vis {
2764 let Some(n) = m.nodes.get(i as usize) else { continue };
2765 let nr = if n.flags & DN_FRAME != 0 { [n.rect[0], n.rect[1], n.rect[2], st.frame_bar.min(n.rect[3])] } else { n.rect };
2767 let q = ipx(st.vp.rect_to_screen(nr));
2768 let (x0, y0) = (q[0].max(r[0]), q[1].max(r[1]));
2769 let (x1, y1) = ((q[0] + q[2]).min(r[0] + r[2]), (q[1] + q[3]).min(r[1] + r[3]));
2770 if x1 <= x0 || y1 <= y0 {
2771 continue;
2772 }
2773 let mut rect = [x0, y0, x1 - x0, y1 - y0];
2774 if self.rtl {
2775 rect[0] = self.rtl_map(rect[0] + rect[2] - 1, r[0], r[2]);
2776 }
2777 let mut state = 0;
2778 if st.sel.contains(&n.key) {
2779 state |= SS_SELECTED;
2780 }
2781 if focused && st.focus == n.key {
2782 state |= SS_FOCUSED;
2783 }
2784 if hov == n.key {
2785 state |= SS_HOVER;
2786 }
2787 let sid = node_sem_id(id, n.key);
2788 if self.m.a11y.pending() && self.a11y_click(sid) {
2789 st.sel.clear();
2790 st.sel.insert(n.key);
2791 st.focus = n.key;
2792 st.pending_sel = true;
2793 self.repass();
2794 }
2795 self.last_set(rect, state as u32);
2796 let l = label(n.key);
2797 self.sem(K_LISTITEM, sid, Some(&l), i as i32);
2798 }
2799 st.visible = vis;
2800 }
2801
2802 #[cfg_attr(jfgui_dev, track_caller)]
2807 pub fn diagram_edge_sems(&mut self, id: u32, st: &mut DiagramState, m: &DModel, label: &dyn Fn(u64) -> String) {
2808 let _src = self.src_scope();
2809 let r = st.vp.rect;
2810 if r[2] <= 0 || r[3] <= 0 {
2811 return;
2812 }
2813 let focused = self.wp.ctx.focus == id;
2814 let hov = match st.hover {
2815 DHit::Edge { key, .. } | DHit::Mid(key, _) | DHit::Waypoint(key, _) => key,
2816 _ => 0,
2817 };
2818 let wr = st.vp.world_rect();
2819 let half = self.dp(6);
2820 let geo = st.ix.geo.clone();
2821 for (i, rt) in geo.routes.iter().enumerate() {
2822 if rt.pts.len() < 2 || !overlaps(rt.bb, wr) || m.edges.get(i).is_none_or(|e| e.key != rt.key) {
2823 continue;
2824 }
2825 let c = st.vp.to_screen(dg_point_at(&rt.pts, 0.5));
2826 let (cx, cy) = (fl(c[0] + 0.5), fl(c[1] + 0.5));
2827 let (x0, y0) = ((cx - half).max(r[0]), (cy - half).max(r[1]));
2828 let (x1, y1) = ((cx + half).min(r[0] + r[2]), (cy + half).min(r[1] + r[3]));
2829 if x1 <= x0 || y1 <= y0 {
2830 continue;
2831 }
2832 let mut rect = [x0, y0, x1 - x0, y1 - y0];
2833 if self.rtl {
2834 rect[0] = self.rtl_map(rect[0] + rect[2] - 1, r[0], r[2]);
2835 }
2836 let mut state = 0;
2837 if st.sel.contains(&rt.key) {
2838 state |= SS_SELECTED;
2839 }
2840 if focused && st.focus == rt.key {
2841 state |= SS_FOCUSED;
2842 }
2843 if hov == rt.key {
2844 state |= SS_HOVER;
2845 }
2846 let sid = node_sem_id(id, rt.key);
2847 if self.m.a11y.pending() && self.a11y_click(sid) {
2848 st.sel.clear();
2849 st.sel.insert(rt.key);
2850 if st.tab_edges {
2851 st.focus = rt.key;
2852 }
2853 st.pending_sel = true;
2854 self.repass();
2855 }
2856 self.last_set(rect, state as u32);
2857 let l = label(rt.key);
2858 self.sem(K_LISTITEM, sid, Some(&l), i as i32);
2859 }
2860 }
2861}