use crate::s2::Point;
use super::graph::Graph;
use super::graph::{
DegenerateEdges, DuplicateEdges, EdgeId, EdgeType, GraphOptions, LabelFetcher, SiblingPairs,
};
use super::id_set_lexicon::IdSetLexicon;
use super::layer::Layer;
use super::{LabelSetId, S2Error};
pub type LabelSetIds = Vec<LabelSetId>;
#[derive(Clone, Debug, PartialEq)]
pub struct Options {
pub duplicate_edges: DuplicateEdges,
}
impl Default for Options {
fn default() -> Self {
Options {
duplicate_edges: DuplicateEdges::Merge,
}
}
}
#[derive(Debug)]
pub struct S2PointVectorLayer {
points: Option<Vec<Point>>,
options: Options,
label_set_ids: Option<LabelSetIds>,
label_set_lexicon: Option<IdSetLexicon>,
track_labels: bool,
#[cfg(test)]
legacy_output: Option<std::rc::Rc<std::cell::RefCell<Vec<Point>>>>,
}
impl S2PointVectorLayer {
pub fn new() -> Self {
S2PointVectorLayer {
points: None,
options: Options::default(),
label_set_ids: None,
label_set_lexicon: None,
track_labels: false,
#[cfg(test)]
legacy_output: None,
}
}
pub fn with_options(options: Options) -> Self {
S2PointVectorLayer {
points: None,
options,
label_set_ids: None,
label_set_lexicon: None,
track_labels: false,
#[cfg(test)]
legacy_output: None,
}
}
pub fn with_labels(options: Options) -> Self {
S2PointVectorLayer {
points: None,
options,
label_set_ids: None,
label_set_lexicon: None,
track_labels: true,
#[cfg(test)]
legacy_output: None,
}
}
#[expect(
clippy::expect_used,
reason = "panics are documented; caller must call build() first"
)]
pub fn into_output(self) -> Vec<Point> {
self.points
.expect("S2PointVectorLayer::build() was not called")
}
pub fn output(&self) -> Option<&[Point]> {
self.points.as_deref()
}
pub fn take_output(&mut self) -> Option<Vec<Point>> {
self.points.take()
}
pub fn label_set_ids(&self) -> Option<&LabelSetIds> {
self.label_set_ids.as_ref()
}
pub fn label_set_lexicon(&self) -> Option<&IdSetLexicon> {
self.label_set_lexicon.as_ref()
}
}
impl Default for S2PointVectorLayer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
impl S2PointVectorLayer {
pub fn new_legacy(output: std::rc::Rc<std::cell::RefCell<Vec<Point>>>) -> Self {
let mut s = Self::new();
s.legacy_output = Some(output);
s
}
pub fn with_options_legacy(
output: std::rc::Rc<std::cell::RefCell<Vec<Point>>>,
options: Options,
) -> Self {
let mut s = Self::with_options(options);
s.legacy_output = Some(output);
s
}
fn sync_legacy(&self) {
if let (Some(output), Some(legacy)) = (&self.points, &self.legacy_output) {
*legacy.borrow_mut() = output.clone();
}
}
}
impl Layer for S2PointVectorLayer {
fn graph_options(&self) -> GraphOptions {
GraphOptions::new(
EdgeType::Directed,
DegenerateEdges::Keep,
self.options.duplicate_edges,
SiblingPairs::Keep,
)
}
fn build(&mut self, graph: &Graph, error: &mut S2Error) {
let fetcher = LabelFetcher::new(graph, EdgeType::Directed);
let mut points = Vec::new();
let mut label_ids: Vec<LabelSetId> = Vec::new();
let mut lex = if self.track_labels {
self.label_set_lexicon.take().unwrap_or_default()
} else {
IdSetLexicon::new()
};
for eid in (0..graph.num_edges().0).map(EdgeId) {
let (v0, v1) = graph.edge(eid);
if v0 != v1 {
*error = S2Error::new(
super::S2ErrorCode::InvalidArgument,
"Found non-degenerate edges".to_string(),
);
continue;
}
points.push(graph.vertex(v0));
if self.track_labels {
let labels = fetcher.fetch(graph, eid);
label_ids.push(lex.add_set(&labels));
}
}
self.points = Some(points);
if self.track_labels {
self.label_set_ids = Some(label_ids);
self.label_set_lexicon = Some(lex);
}
#[cfg(test)]
self.sync_legacy();
}
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::s2::text_format::parse_point;
#[test]
fn test_point_vector_merge_duplicates() {
use super::super::S2Builder;
let mut builder = S2Builder::new(super::super::Options::default());
let opts = Options {
duplicate_edges: DuplicateEdges::Merge,
};
builder.start_layer(Box::new(S2PointVectorLayer::with_options(opts)));
builder.add_point(parse_point("0:1"));
builder.add_point(parse_point("0:2"));
builder.add_point(parse_point("0:1")); builder.add_point(parse_point("0:4"));
builder.add_point(parse_point("0:5"));
builder.add_point(parse_point("0:5")); builder.add_point(parse_point("0:6"));
let mut layers = builder.build().expect("build failed");
let layer = layers
.remove(0)
.into_any()
.downcast::<S2PointVectorLayer>()
.expect("wrong layer type");
assert_eq!(layer.into_output().len(), 5);
}
#[test]
fn test_point_vector_keep_duplicates() {
use super::super::S2Builder;
let mut builder = S2Builder::new(super::super::Options::default());
let opts = Options {
duplicate_edges: DuplicateEdges::Keep,
};
builder.start_layer(Box::new(S2PointVectorLayer::with_options(opts)));
builder.add_point(parse_point("0:1"));
builder.add_point(parse_point("0:2"));
builder.add_point(parse_point("0:1"));
builder.add_point(parse_point("0:4"));
builder.add_point(parse_point("0:5"));
builder.add_point(parse_point("0:5"));
builder.add_point(parse_point("0:6"));
let mut layers = builder.build().expect("build failed");
let layer = layers
.remove(0)
.into_any()
.downcast::<S2PointVectorLayer>()
.expect("wrong layer type");
assert_eq!(layer.into_output().len(), 7);
}
#[test]
fn test_point_vector_error_non_degenerate() {
use super::super::{S2Builder, S2ErrorCode};
let mut builder = S2Builder::new(super::super::Options::default());
let opts = Options {
duplicate_edges: DuplicateEdges::Keep,
};
builder.start_layer(Box::new(S2PointVectorLayer::with_options(opts)));
builder.add_point(parse_point("0:1"));
builder.add_edge(parse_point("0:3"), parse_point("0:4"));
builder.add_point(parse_point("0:5"));
let result = builder.build();
assert!(result.is_err(), "expected build to fail");
let err = result.unwrap_err();
assert_eq!(err.code, S2ErrorCode::InvalidArgument);
}
}
#[cfg(test)]
#[path = "point_vector_layer_tests.rs"]
mod point_vector_layer_tests;