geometry: create bboxes for groupings

This commit is contained in:
Mikolaj Wielgus 2024-03-28 01:31:12 +00:00
parent 3a812d5621
commit 31189a9f5e
4 changed files with 97 additions and 64 deletions

View File

@ -27,7 +27,7 @@ use crate::drawing::{
SeqLooseSegIndex, SeqLooseSegWeight, SeqLooseSegIndex, SeqLooseSegWeight,
}, },
}; };
use crate::geometry::Compound; use crate::geometry::Node;
use crate::geometry::{ use crate::geometry::{
shape::{Shape, ShapeTrait}, shape::{Shape, ShapeTrait},
with_rtree::GeometryWithRtree, with_rtree::GeometryWithRtree,
@ -637,7 +637,7 @@ impl<R: RulesTrait> Drawing<R> {
.rtree() .rtree()
.iter() .iter()
.filter_map(|wrapper| { .filter_map(|wrapper| {
if let Compound::Primitive(primitive_node) = wrapper.data { if let Node::Primitive(primitive_node) = wrapper.data {
Some(primitive_node) Some(primitive_node)
} else { } else {
None None
@ -653,7 +653,7 @@ impl<R: RulesTrait> Drawing<R> {
[f64::INFINITY, f64::INFINITY, layer as f64], [f64::INFINITY, f64::INFINITY, layer as f64],
)) ))
.filter_map(|wrapper| { .filter_map(|wrapper| {
if let Compound::Primitive(primitive_node) = wrapper.data { if let Node::Primitive(primitive_node) = wrapper.data {
Some(primitive_node) Some(primitive_node)
} else { } else {
None None
@ -748,7 +748,7 @@ impl<R: RulesTrait> Drawing<R> {
.rtree() .rtree()
.locate_in_envelope_intersecting(&limiting_shape.full_height_envelope_3d(0.0, 2)) .locate_in_envelope_intersecting(&limiting_shape.full_height_envelope_3d(0.0, 2))
.filter_map(|wrapper| { .filter_map(|wrapper| {
if let Compound::Primitive(primitive_node) = wrapper.data { if let Node::Primitive(primitive_node) = wrapper.data {
Some(primitive_node) Some(primitive_node)
} else { } else {
None None
@ -783,7 +783,7 @@ impl<R: RulesTrait> Drawing<R> {
.rtree() .rtree()
.locate_in_envelope_intersecting(&shape.full_height_envelope_3d(0.0, 2)) .locate_in_envelope_intersecting(&shape.full_height_envelope_3d(0.0, 2))
.filter_map(|wrapper| { .filter_map(|wrapper| {
if let Compound::Primitive(primitive_node) = wrapper.data { if let Node::Primitive(primitive_node) = wrapper.data {
Some(primitive_node) Some(primitive_node)
} else { } else {
None None

View File

@ -20,7 +20,7 @@ use crate::{
}, },
Drawing, Drawing,
}, },
geometry::Compound, geometry::Node,
}; };
#[enum_dispatch] #[enum_dispatch]
@ -184,7 +184,7 @@ impl<'a, W, R: RulesTrait> GenericPrimitive<'a, W, R> {
} }
fn tagged_weight(&self) -> PrimitiveWeight { fn tagged_weight(&self) -> PrimitiveWeight {
if let Compound::Primitive(weight) = *self if let Node::Primitive(weight) = *self
.drawing .drawing
.geometry() .geometry()
.graph() .graph()
@ -258,15 +258,9 @@ impl<'a, R: RulesTrait> FixedDot<'a, R> {
.graph() .graph()
.node_weight(ni.node_index()) .node_weight(ni.node_index())
.unwrap(); .unwrap();
if matches!( if matches!(weight, Node::Primitive(PrimitiveWeight::LoneLooseSeg(..))) {
weight,
Compound::Primitive(PrimitiveWeight::LoneLooseSeg(..))
) {
Some(LoneLooseSegIndex::new(ni.node_index()).into()) Some(LoneLooseSegIndex::new(ni.node_index()).into())
} else if matches!( } else if matches!(weight, Node::Primitive(PrimitiveWeight::SeqLooseSeg(..))) {
weight,
Compound::Primitive(PrimitiveWeight::SeqLooseSeg(..))
) {
Some(SeqLooseSegIndex::new(ni.node_index()).into()) Some(SeqLooseSegIndex::new(ni.node_index()).into())
} else { } else {
None None

View File

@ -56,9 +56,9 @@ pub enum GeometryLabel {
} }
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Compound<PW, XW> { pub enum Node<PW, GW> {
Primitive(PW), Primitive(PW),
Grouping(XW), Grouping(GW),
} }
pub trait DotWeightTrait<GW>: GetPos + SetPos + GetWidth + Into<GW> + Copy {} pub trait DotWeightTrait<GW>: GetPos + SetPos + GetWidth + Into<GW> + Copy {}
@ -77,7 +77,7 @@ pub struct Geometry<
SI: GetNodeIndex + Into<PI> + Copy, SI: GetNodeIndex + Into<PI> + Copy,
BI: GetNodeIndex + Into<PI> + Copy, BI: GetNodeIndex + Into<PI> + Copy,
> { > {
graph: StableDiGraph<Compound<PW, GW>, GeometryLabel, usize>, graph: StableDiGraph<Node<PW, GW>, GeometryLabel, usize>,
weight_marker: PhantomData<PW>, weight_marker: PhantomData<PW>,
dot_weight_marker: PhantomData<DW>, dot_weight_marker: PhantomData<DW>,
seg_weight_marker: PhantomData<SW>, seg_weight_marker: PhantomData<SW>,
@ -117,7 +117,7 @@ impl<
} }
pub fn add_dot<W: DotWeightTrait<PW>>(&mut self, weight: W) -> GenericIndex<W> { pub fn add_dot<W: DotWeightTrait<PW>>(&mut self, weight: W) -> GenericIndex<W> {
GenericIndex::<W>::new(self.graph.add_node(Compound::Primitive(weight.into()))) GenericIndex::<W>::new(self.graph.add_node(Node::Primitive(weight.into())))
} }
pub fn add_seg<W: SegWeightTrait<PW>>( pub fn add_seg<W: SegWeightTrait<PW>>(
@ -126,7 +126,7 @@ impl<
to: DI, to: DI,
weight: W, weight: W,
) -> GenericIndex<W> { ) -> GenericIndex<W> {
let seg = GenericIndex::<W>::new(self.graph.add_node(Compound::Primitive(weight.into()))); let seg = GenericIndex::<W>::new(self.graph.add_node(Node::Primitive(weight.into())));
self.graph self.graph
.update_edge(from.node_index(), seg.node_index(), GeometryLabel::Joined); .update_edge(from.node_index(), seg.node_index(), GeometryLabel::Joined);
@ -143,7 +143,7 @@ impl<
core: DI, core: DI,
weight: W, weight: W,
) -> GenericIndex<W> { ) -> GenericIndex<W> {
let bend = GenericIndex::<W>::new(self.graph.add_node(Compound::Primitive(weight.into()))); let bend = GenericIndex::<W>::new(self.graph.add_node(Node::Primitive(weight.into())));
self.graph self.graph
.update_edge(from.node_index(), bend.node_index(), GeometryLabel::Joined); .update_edge(from.node_index(), bend.node_index(), GeometryLabel::Joined);
@ -156,7 +156,7 @@ impl<
} }
pub fn add_grouping(&mut self, weight: GW) -> GenericIndex<GW> { pub fn add_grouping(&mut self, weight: GW) -> GenericIndex<GW> {
GenericIndex::<GW>::new(self.graph.add_node(Compound::Grouping(weight))) GenericIndex::<GW>::new(self.graph.add_node(Node::Grouping(weight)))
} }
pub fn assign_to_grouping<W>( pub fn assign_to_grouping<W>(
@ -182,14 +182,13 @@ impl<
pub fn move_dot(&mut self, dot: DI, to: Point) { pub fn move_dot(&mut self, dot: DI, to: Point) {
let mut weight = self.dot_weight(dot); let mut weight = self.dot_weight(dot);
weight.set_pos(to); weight.set_pos(to);
*self.graph.node_weight_mut(dot.node_index()).unwrap() = Compound::Primitive(weight.into()); *self.graph.node_weight_mut(dot.node_index()).unwrap() = Node::Primitive(weight.into());
} }
pub fn shift_bend(&mut self, bend: BI, offset: f64) { pub fn shift_bend(&mut self, bend: BI, offset: f64) {
let mut weight = self.bend_weight(bend); let mut weight = self.bend_weight(bend);
weight.set_offset(offset); weight.set_offset(offset);
*self.graph.node_weight_mut(bend.node_index()).unwrap() = *self.graph.node_weight_mut(bend.node_index()).unwrap() = Node::Primitive(weight.into());
Compound::Primitive(weight.into());
} }
pub fn flip_bend(&mut self, bend: BI) { pub fn flip_bend(&mut self, bend: BI) {
@ -285,7 +284,7 @@ impl<
} }
fn primitive_weight(&self, index: NodeIndex<usize>) -> PW { fn primitive_weight(&self, index: NodeIndex<usize>) -> PW {
if let Compound::Primitive(weight) = *self.graph.node_weight(index).unwrap() { if let Node::Primitive(weight) = *self.graph.node_weight(index).unwrap() {
weight weight
} else { } else {
unreachable!() unreachable!()
@ -310,6 +309,14 @@ impl<
.unwrap_or_else(|_| unreachable!()) .unwrap_or_else(|_| unreachable!())
} }
fn grouping_weight(&self, grouping: GenericIndex<GW>) -> GW {
if let Node::Grouping(weight) = *self.graph.node_weight(grouping.node_index()).unwrap() {
weight
} else {
unreachable!()
}
}
fn core_weight(&self, bend: BI) -> DW { fn core_weight(&self, bend: BI) -> DW {
self.graph self.graph
.neighbors(bend.node_index()) .neighbors(bend.node_index())
@ -393,7 +400,7 @@ impl<
pub fn outer(&self, bend: BI) -> Option<BI> { pub fn outer(&self, bend: BI) -> Option<BI> {
self.graph self.graph
.neighbors_directed(bend.node_index(), Outgoing) .neighbors(bend.node_index())
.filter(|ni| { .filter(|ni| {
matches!( matches!(
self.graph self.graph
@ -459,7 +466,26 @@ impl<
self.joineds(dot.into()).filter_map(|ni| ni.try_into().ok()) self.joineds(dot.into()).filter_map(|ni| ni.try_into().ok())
} }
pub fn graph(&self) -> &StableDiGraph<Compound<PW, GW>, GeometryLabel, usize> { pub fn members(&self, grouping: GenericIndex<GW>) -> impl Iterator<Item = PI> + '_ {
self.graph
.neighbors(grouping.node_index())
.filter(move |ni| {
matches!(
self.graph
.edge_weight(self.graph.find_edge(grouping.node_index(), *ni).unwrap())
.unwrap(),
GeometryLabel::Grouping
)
})
.map(|ni| {
self.primitive_weight(ni)
.retag(ni)
.try_into()
.unwrap_or_else(|_| unreachable!())
})
}
pub fn graph(&self) -> &StableDiGraph<Node<PW, GW>, GeometryLabel, usize> {
&self.graph &self.graph
} }
} }

View File

@ -3,21 +3,20 @@ use std::marker::PhantomData;
use contracts::debug_invariant; use contracts::debug_invariant;
use geo::Point; use geo::Point;
use petgraph::stable_graph::StableDiGraph; use petgraph::stable_graph::StableDiGraph;
use rstar::{primitives::GeomWithData, RTree, RTreeObject, AABB}; use rstar::{primitives::GeomWithData, Envelope, RTree, RTreeObject, AABB};
use crate::{ use crate::{
drawing::graph::{GetLayer, Retag}, drawing::graph::{GetLayer, Retag},
geometry::{ geometry::{
shape::{Shape, ShapeTrait}, shape::{Shape, ShapeTrait},
BendWeightTrait, Compound, DotWeightTrait, Geometry, GeometryLabel, GetWidth, BendWeightTrait, DotWeightTrait, Geometry, GeometryLabel, GetWidth, Node, SegWeightTrait,
SegWeightTrait,
}, },
graph::{GenericIndex, GetNodeIndex}, graph::{GenericIndex, GetNodeIndex},
}; };
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Bbox { pub struct Bbox {
aabb: AABB<[f64; 3]>, pub aabb: AABB<[f64; 3]>,
} }
impl Bbox { impl Bbox {
@ -48,7 +47,7 @@ pub struct GeometryWithRtree<
BI: GetNodeIndex + Into<PI> + Copy, BI: GetNodeIndex + Into<PI> + Copy,
> { > {
geometry: Geometry<PW, DW, SW, BW, GW, PI, DI, SI, BI>, geometry: Geometry<PW, DW, SW, BW, GW, PI, DI, SI, BI>,
rtree: RTree<BboxedIndex<Compound<PI, GenericIndex<GW>>>>, rtree: RTree<BboxedIndex<Node<PI, GenericIndex<GW>>>>,
layer_count: u64, layer_count: u64,
weight_marker: PhantomData<PW>, weight_marker: PhantomData<PW>,
dot_weight_marker: PhantomData<DW>, dot_weight_marker: PhantomData<DW>,
@ -101,7 +100,7 @@ impl<
.dot_shape(dot.into().try_into().unwrap_or_else(|_| unreachable!())) .dot_shape(dot.into().try_into().unwrap_or_else(|_| unreachable!()))
.envelope_3d(0.0, weight.layer()), .envelope_3d(0.0, weight.layer()),
), ),
Compound::Primitive(dot.into()), Node::Primitive(dot.into()),
)); ));
dot dot
} }
@ -122,7 +121,7 @@ impl<
.seg_shape(seg.into().try_into().unwrap_or_else(|_| unreachable!())) .seg_shape(seg.into().try_into().unwrap_or_else(|_| unreachable!()))
.envelope_3d(0.0, weight.layer()), .envelope_3d(0.0, weight.layer()),
), ),
Compound::Primitive(seg.into()), Node::Primitive(seg.into()),
)); ));
seg seg
} }
@ -144,17 +143,14 @@ impl<
.bend_shape(bend.into().try_into().unwrap_or_else(|_| unreachable!())) .bend_shape(bend.into().try_into().unwrap_or_else(|_| unreachable!()))
.envelope_3d(0.0, weight.layer()), .envelope_3d(0.0, weight.layer()),
), ),
Compound::Primitive(bend.into()), Node::Primitive(bend.into()),
)); ));
bend bend
} }
pub fn add_grouping(&mut self, weight: GW) -> GenericIndex<GW> { pub fn add_grouping(&mut self, weight: GW) -> GenericIndex<GW> {
let grouping = self.geometry.add_grouping(weight); let grouping = self.geometry.add_grouping(weight);
self.rtree.insert(BboxedIndex::new( self.rtree.insert(self.make_grouping_bbox(grouping));
Bbox::new(AABB::<[f64; 3]>::from_point([0.0, 0.0, -1.0].into())),
Compound::Grouping(grouping),
));
grouping grouping
} }
@ -163,7 +159,9 @@ impl<
primitive: GenericIndex<W>, primitive: GenericIndex<W>,
grouping: GenericIndex<GW>, grouping: GenericIndex<GW>,
) { ) {
self.geometry.assign_to_grouping(primitive, grouping) self.rtree.remove(&self.make_grouping_bbox(grouping));
self.geometry.assign_to_grouping(primitive, grouping);
self.rtree.insert(self.make_grouping_bbox(grouping));
} }
pub fn remove_dot(&mut self, dot: DI) -> Result<(), ()> { pub fn remove_dot(&mut self, dot: DI) -> Result<(), ()> {
@ -275,67 +273,82 @@ impl<
BI: GetNodeIndex + Into<PI> + Copy, BI: GetNodeIndex + Into<PI> + Copy,
> GeometryWithRtree<PW, DW, SW, BW, GW, PI, DI, SI, BI> > GeometryWithRtree<PW, DW, SW, BW, GW, PI, DI, SI, BI>
{ {
fn make_dot_bbox(&self, dot: DI) -> BboxedIndex<Compound<PI, GenericIndex<GW>>> { fn make_bbox(&self, primitive: PI) -> BboxedIndex<Node<PI, GenericIndex<GW>>> {
if let Ok(dot) = <PI as TryInto<DI>>::try_into(primitive) {
self.make_dot_bbox(dot)
} else if let Ok(seg) = <PI as TryInto<SI>>::try_into(primitive) {
self.make_seg_bbox(seg)
} else if let Ok(bend) = <PI as TryInto<BI>>::try_into(primitive) {
self.make_bend_bbox(bend)
} else {
unreachable!();
}
}
fn make_dot_bbox(&self, dot: DI) -> BboxedIndex<Node<PI, GenericIndex<GW>>> {
BboxedIndex::new( BboxedIndex::new(
Bbox::new( Bbox::new(
self.geometry self.geometry
.dot_shape(dot) .dot_shape(dot)
.envelope_3d(0.0, self.layer(dot.into())), .envelope_3d(0.0, self.layer(dot.into())),
), ),
Compound::Primitive(dot.into()), Node::Primitive(dot.into()),
) )
} }
fn make_seg_bbox(&self, seg: SI) -> BboxedIndex<Compound<PI, GenericIndex<GW>>> { fn make_seg_bbox(&self, seg: SI) -> BboxedIndex<Node<PI, GenericIndex<GW>>> {
BboxedIndex::new( BboxedIndex::new(
Bbox::new( Bbox::new(
self.geometry self.geometry
.seg_shape(seg) .seg_shape(seg)
.envelope_3d(0.0, self.layer(seg.into())), .envelope_3d(0.0, self.layer(seg.into())),
), ),
Compound::Primitive(seg.into()), Node::Primitive(seg.into()),
) )
} }
fn make_bend_bbox(&self, bend: BI) -> BboxedIndex<Compound<PI, GenericIndex<GW>>> { fn make_bend_bbox(&self, bend: BI) -> BboxedIndex<Node<PI, GenericIndex<GW>>> {
BboxedIndex::new( BboxedIndex::new(
Bbox::new( Bbox::new(
self.geometry self.geometry
.bend_shape(bend) .bend_shape(bend)
.envelope_3d(0.0, self.layer(bend.into())), .envelope_3d(0.0, self.layer(bend.into())),
), ),
Compound::Primitive(bend.into()), Node::Primitive(bend.into()),
) )
} }
fn make_grouping_bbox( fn make_grouping_bbox(
&self, &self,
grouping: GenericIndex<GW>, grouping: GenericIndex<GW>,
) -> BboxedIndex<Compound<PI, GenericIndex<GW>>> { ) -> BboxedIndex<Node<PI, GenericIndex<GW>>> {
BboxedIndex::new( let mut aabb = AABB::<[f64; 3]>::new_empty();
Bbox::new(AABB::<[f64; 3]>::from_point([0.0, 0.0, -1.0].into())),
Compound::Grouping(grouping), for member in self.geometry.members(grouping) {
) aabb.merge(&self.make_bbox(member).geom().aabb);
}
BboxedIndex::new(Bbox::new(aabb), Node::Grouping(grouping))
} }
fn shape(&self, index: PI) -> Shape { fn shape(&self, primitive: PI) -> Shape {
if let Ok(dot) = <PI as TryInto<DI>>::try_into(index) { if let Ok(dot) = <PI as TryInto<DI>>::try_into(primitive) {
self.geometry.dot_shape(dot) self.geometry.dot_shape(dot)
} else if let Ok(seg) = <PI as TryInto<SI>>::try_into(index) { } else if let Ok(seg) = <PI as TryInto<SI>>::try_into(primitive) {
self.geometry.seg_shape(seg) self.geometry.seg_shape(seg)
} else if let Ok(bend) = <PI as TryInto<BI>>::try_into(index) { } else if let Ok(bend) = <PI as TryInto<BI>>::try_into(primitive) {
self.geometry.bend_shape(bend) self.geometry.bend_shape(bend)
} else { } else {
unreachable!(); unreachable!();
} }
} }
fn layer(&self, index: PI) -> u64 { fn layer(&self, primitive: PI) -> u64 {
if let Ok(dot) = <PI as TryInto<DI>>::try_into(index) { if let Ok(dot) = <PI as TryInto<DI>>::try_into(primitive) {
self.geometry.dot_weight(dot).layer() self.geometry.dot_weight(dot).layer()
} else if let Ok(seg) = <PI as TryInto<SI>>::try_into(index) { } else if let Ok(seg) = <PI as TryInto<SI>>::try_into(primitive) {
self.geometry.seg_weight(seg).layer() self.geometry.seg_weight(seg).layer()
} else if let Ok(bend) = <PI as TryInto<BI>>::try_into(index) { } else if let Ok(bend) = <PI as TryInto<BI>>::try_into(primitive) {
self.geometry.bend_weight(bend).layer() self.geometry.bend_weight(bend).layer()
} else { } else {
unreachable!(); unreachable!();
@ -346,25 +359,25 @@ impl<
&self.geometry &self.geometry
} }
pub fn rtree(&self) -> &RTree<BboxedIndex<Compound<PI, GenericIndex<GW>>>> { pub fn rtree(&self) -> &RTree<BboxedIndex<Node<PI, GenericIndex<GW>>>> {
&self.rtree &self.rtree
} }
pub fn graph(&self) -> &StableDiGraph<Compound<PW, GW>, GeometryLabel, usize> { pub fn graph(&self) -> &StableDiGraph<Node<PW, GW>, GeometryLabel, usize> {
self.geometry.graph() self.geometry.graph()
} }
fn test_envelopes(&self) -> bool { fn test_envelopes(&self) -> bool {
!self.rtree.iter().any(|wrapper| { !self.rtree.iter().any(|wrapper| {
// TODO: Test envelopes of groupings too. // TODO: Test envelopes of groupings too.
let Compound::Primitive(primitive_node) = wrapper.data else { let Node::Primitive(primitive_node) = wrapper.data else {
return false; return false;
}; };
let shape = self.shape(primitive_node); let shape = self.shape(primitive_node);
let layer = self.layer(primitive_node); let layer = self.layer(primitive_node);
let wrapper = BboxedIndex::new( let wrapper = BboxedIndex::new(
Bbox::new(shape.envelope_3d(0.0, layer)), Bbox::new(shape.envelope_3d(0.0, layer)),
Compound::Primitive(primitive_node), Node::Primitive(primitive_node),
); );
!self !self
.rtree .rtree