mirror of https://codeberg.org/topola/topola.git
305 lines
9.2 KiB
Rust
305 lines
9.2 KiB
Rust
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<SegbendHead, ()> {
|
|
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<SegbendHead, ()> {
|
|
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<SegbendHead, ()> {
|
|
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<Head, ()> {
|
|
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<SegbendHead, ()> {
|
|
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<Head> {
|
|
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)
|
|
}
|
|
}
|