use contracts::debug_ensures; use enum_dispatch::enum_dispatch; use geo::{EuclideanLength, Point}; use crate::{ graph::{ BendIndex, DotIndex, FixedDotIndex, FixedSegWeight, GetEnds, GetNet, Index, LooseBendIndex, LooseBendWeight, LooseDotIndex, LooseDotWeight, LooseSegWeight, MakePrimitive, }, guide::Guide, layout::Layout, math::Circle, primitive::{GetOtherEnd, GetWeight}, rules::{Conditions, Rules}, segbend::Segbend, }; #[enum_dispatch] pub trait HeadTrait { fn dot(&self) -> DotIndex; } #[enum_dispatch(HeadTrait)] #[derive(Debug, Clone, Copy)] pub enum Head { Bare(BareHead), Segbend(SegbendHead), } #[derive(Debug, Clone, Copy)] pub struct BareHead { pub dot: FixedDotIndex, } impl HeadTrait for BareHead { fn dot(&self) -> DotIndex { self.dot.into() } } #[derive(Debug, Clone, Copy)] pub struct SegbendHead { pub dot: LooseDotIndex, pub segbend: Segbend, } impl HeadTrait for SegbendHead { fn dot(&self) -> DotIndex { self.dot.into() } } pub struct Draw<'a> { layout: &'a mut Layout, rules: &'a Rules, } impl<'a> Draw<'a> { pub fn new(layout: &'a mut Layout, rules: &'a Rules) -> Self { Self { layout, rules } } pub fn start(&mut self, from: LooseDotIndex) -> Head { self.segbend_head(from).into() } #[debug_ensures(ret.is_ok() -> self.layout.node_count() == old(self.layout.node_count() + 1))] #[debug_ensures(ret.is_err() -> self.layout.node_count() == old(self.layout.node_count()))] pub fn finish_in_dot(&mut self, head: Head, into: FixedDotIndex, width: f64) -> Result<(), ()> { let tangent = self .guide(&Default::default()) .head_into_dot_segment(&head, into, width)?; let head = self.extend_head(head, tangent.start_point())?; let net = head.dot().primitive(&self.layout.graph).net(); match head.dot() { DotIndex::Fixed(dot) => { self.layout .add_fixed_seg(into.into(), dot, FixedSegWeight { net, width })?; } DotIndex::Loose(dot) => { self.layout .add_loose_seg(into.into(), dot, LooseSegWeight { net })?; } } Ok(()) } #[debug_ensures(ret.is_ok() -> self.layout.node_count() == old(self.layout.node_count() + 1))] #[debug_ensures(ret.is_err() -> self.layout.node_count() == old(self.layout.node_count()))] pub fn finish_in_bend( &mut self, head: Head, into_bend: LooseBendIndex, into: LooseDotIndex, width: f64, ) -> Result<(), ()> { let to_head = self.segbend_head(into); let to_cw = self .guide(&Default::default()) .head_cw(&to_head.into()) .unwrap(); let tangent = self.guide(&Default::default()).head_around_bend_segment( &head, into_bend.into(), to_cw, width, )?; let head = self.extend_head(head, tangent.start_point())?; let _to_head = self.extend_head(to_head.into(), tangent.end_point())?; let net = head.dot().primitive(&self.layout.graph).net(); self.layout .add_loose_seg(head.dot(), into.into(), LooseSegWeight { net })?; Ok(()) } #[debug_ensures(ret.is_ok() -> self.layout.node_count() == old(self.layout.node_count() + 4))] #[debug_ensures(ret.is_err() -> self.layout.node_count() == old(self.layout.node_count()))] pub fn segbend_around_dot( &mut self, head: Head, around: DotIndex, width: f64, ) -> Result { let mut tangents = self .guide(&Default::default()) .head_around_dot_segments(&head, around, width)?; let mut dirs = [true, false]; if tangents.1.euclidean_length() < tangents.0.euclidean_length() { tangents = (tangents.1, tangents.0); dirs = [false, true]; } [tangents.0, tangents.1] .iter() .enumerate() .find_map(|(i, tangent)| { self.segbend_around( head, around.into(), tangent.start_point(), tangent.end_point(), dirs[i], width, ) .ok() }) .ok_or(()) } #[debug_ensures(ret.is_ok() -> self.layout.node_count() == old(self.layout.node_count() + 4))] #[debug_ensures(ret.is_err() -> self.layout.node_count() == old(self.layout.node_count()))] pub fn segbend_around_bend( &mut self, head: Head, around: BendIndex, width: f64, ) -> Result { let mut tangents = self.guide(&Default::default()).head_around_bend_segments( &head, around.into(), width, )?; let mut dirs = [true, false]; if tangents.1.euclidean_length() < tangents.0.euclidean_length() { tangents = (tangents.1, tangents.0); dirs = [false, true]; } [tangents.0, tangents.1] .iter() .enumerate() .find_map(|(i, tangent)| { self.segbend_around( head, around.into(), tangent.start_point(), tangent.end_point(), dirs[i], width, ) .ok() }) .ok_or(()) } #[debug_ensures(ret.is_ok() -> self.layout.node_count() == old(self.layout.node_count() + 4))] #[debug_ensures(ret.is_err() -> self.layout.node_count() == old(self.layout.node_count()))] fn segbend_around( &mut self, head: Head, around: Index, from: Point, to: Point, cw: bool, width: f64, ) -> Result { let head = self.extend_head(head, from)?; self.segbend(head, around, to, cw, width) } #[debug_ensures(self.layout.node_count() == old(self.layout.node_count()))] fn extend_head(&mut self, head: Head, to: Point) -> Result { if let Head::Segbend(head) = head { self.layout.move_dot(head.dot, to)?; Ok(Head::Segbend(head)) } else { Ok(head) } } #[debug_ensures(ret.is_ok() -> self.layout.node_count() == old(self.layout.node_count() + 4))] #[debug_ensures(ret.is_err() -> self.layout.node_count() == old(self.layout.node_count()))] fn segbend( &mut self, head: Head, around: Index, to: Point, cw: bool, width: f64, ) -> Result { let net = head.dot().primitive(&self.layout.graph).net(); let segbend = self.layout.add_segbend( head.dot(), around, LooseDotWeight { net, circle: Circle { pos: to, r: width / 2.0, }, }, LooseSegWeight { net }, LooseBendWeight { net, cw }, )?; Ok(SegbendHead { dot: self.layout.primitive(segbend.bend).other_end(segbend.dot), segbend, }) } #[debug_ensures(ret.is_some() -> self.layout.node_count() == old(self.layout.node_count() - 4))] #[debug_ensures(ret.is_none() -> self.layout.node_count() == old(self.layout.node_count()))] pub fn undo_segbend(&mut self, head: SegbendHead) -> Option { let prev_dot = self .layout .primitive(head.segbend.seg) .other_end(head.segbend.dot.into()); self.layout.remove_interior(&head.segbend); self.layout.remove(head.dot().into()); Some(self.head(prev_dot)) } #[debug_ensures(self.layout.node_count() == old(self.layout.node_count()))] pub fn update_bow(&mut self, bend: LooseBendIndex) { /*let cw = self.layout.primitive(bend).weight().cw; let ends = self.layout.primitive(bend).ends(); let from_head = self.rear_head(ends.0); let to_head = self.rear_head(ends.1); let from = self .guide(&Default::default()) .head_around_bend_segment(&from_head, inner, cw, 3.0); let to = self .guide(&Default::default()) .head_around_bend_segment(&from_head, inner, cw, 3.0); self.layout.reposition_bend(bend, from, to);*/ } fn head(&self, dot: DotIndex) -> Head { match dot { DotIndex::Fixed(loose) => BareHead { dot: loose }.into(), DotIndex::Loose(fixed) => self.segbend_head(fixed).into(), } } fn segbend_head(&self, dot: LooseDotIndex) -> SegbendHead { SegbendHead { dot, segbend: self.layout.segbend(dot), } } fn rear_head(&self, dot: LooseDotIndex) -> Head { self.head(self.rear(self.segbend_head(dot))) } fn rear(&self, head: SegbendHead) -> DotIndex { self.layout .primitive(head.segbend.seg) .other_end(head.segbend.dot.into()) } fn guide(&'a self, conditions: &'a Conditions) -> Guide { Guide::new(self.layout, self.rules, conditions) } }