kopia lustrzana https://github.com/iliis/svg2gerber
Basic functionality is there :)
commit
b06d4876f0
|
@ -0,0 +1,4 @@
|
|||
|
||||
/target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "svg2gerber"
|
||||
version = "0.1.0"
|
||||
authors = ["Samuel Bryner <samuelbryner@gmx.ch>"]
|
||||
|
||||
[dependencies]
|
||||
#gerber-types = { git = "https://github.com/dbrgn/gerber-types-rs" }
|
||||
gerber-types = "0.1"
|
||||
usvg = "0.5"
|
||||
svgdom = "0.16"
|
||||
lyon = "0.11"
|
||||
conv = "0.3" # this is the version used by gerber-types
|
||||
gnuplot = "0.0.26" # just for debugging
|
||||
log = "0.4"
|
||||
env_logger = "0.6"
|
||||
|
||||
[patch.crates-io]
|
||||
gerber-types = { path = "../gerber-types-rs" }
|
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,153 @@
|
|||
extern crate lyon;
|
||||
|
||||
use sort_polygons::Polygon;
|
||||
|
||||
use gerber_types::*;
|
||||
|
||||
use std::io::Write;
|
||||
use conv::TryFrom;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GerberBuilder {
|
||||
cf: gerber_types::CoordinateFormat,
|
||||
commands: Vec<gerber_types::Command>,
|
||||
}
|
||||
|
||||
|
||||
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
|
||||
pub trait IntoCommand {
|
||||
fn into_command(self) -> gerber_types::Command;
|
||||
}
|
||||
|
||||
impl IntoCommand for FunctionCode {
|
||||
fn into_command(self) -> gerber_types::Command {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoCommand for gerber_types::GCode {
|
||||
fn into_command(self) -> gerber_types::Command {
|
||||
gerber_types::FunctionCode::GCode(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl IntoCommand for gerber_types::ExtendedCode {
|
||||
fn into_command(self) -> gerber_types::Command {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoCommand for gerber_types::Operation {
|
||||
fn into_command(self) -> gerber_types::Command {
|
||||
gerber_types::FunctionCode::DCode(
|
||||
gerber_types::DCode::Operation(self)
|
||||
).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl GerberBuilder {
|
||||
pub fn new(cf: gerber_types::CoordinateFormat, layer_type: Part, layer_func: FileFunction, file_polarity: bool) -> Self { GerberBuilder {
|
||||
cf: cf,
|
||||
commands: vec![
|
||||
|
||||
// gerbv doesn't seem to parse these:
|
||||
ExtendedCode::FileAttribute(
|
||||
FileAttribute::GenerationSoftware(
|
||||
GenerationSoftware::new("SAM", "svg2gerber", Some(VERSION))
|
||||
)
|
||||
).into(),
|
||||
|
||||
ExtendedCode::FileAttribute(
|
||||
FileAttribute::Part(layer_type)
|
||||
).into(),
|
||||
|
||||
ExtendedCode::FileAttribute(
|
||||
FileAttribute::FileFunction(layer_func)
|
||||
).into(),
|
||||
|
||||
ExtendedCode::FileAttribute(
|
||||
FileAttribute::FilePolarity(if file_polarity {FilePolarity::Positive} else {FilePolarity::Negative})
|
||||
).into(),
|
||||
|
||||
//FunctionCode::GCode( GCode::Comment("Ucamco ex. 1: Two square boxes".to_string())).into(),
|
||||
ExtendedCode::CoordinateFormat(cf).into(),
|
||||
ExtendedCode::Unit(Unit::Millimeters).into(),
|
||||
|
||||
// gerbv complains if there are no apertures defined
|
||||
ExtendedCode::ApertureDefinition(
|
||||
ApertureDefinition {
|
||||
code: 10,
|
||||
aperture: Aperture::Circle(Circle { diameter: 0.01, hole_diameter: None }),
|
||||
}
|
||||
).into(),
|
||||
|
||||
ExtendedCode::LoadPolarity(Polarity::Dark).into(), // this is the default, this makes our intentions explicit
|
||||
FunctionCode::GCode(
|
||||
GCode::InterpolationMode(InterpolationMode::Linear)
|
||||
).into(),
|
||||
],
|
||||
} }
|
||||
|
||||
// this function should not require a mutable self, but then how can we call it in
|
||||
// a function that mutates self?
|
||||
pub fn vertex_to_coords(&self, vertex: &lyon::math::Point) -> Coordinates {
|
||||
// seriously?! this gerber library seems unnecessarily complicated
|
||||
Coordinates::new(
|
||||
CoordinateNumber::try_from( vertex.x as f64).unwrap(),
|
||||
// mirror vertical axis, as SVG and gerber use different conventions
|
||||
// TODO: this is a bit hackish, no? where shall we put this instead?
|
||||
CoordinateNumber::try_from(-vertex.y as f64).unwrap(),
|
||||
self.cf
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
pub fn push<F: IntoCommand>(&mut self, cmd: F) {
|
||||
self.commands.push(cmd.into_command());
|
||||
}
|
||||
|
||||
pub fn publish<W: Write>(&mut self, writer: &mut W) {
|
||||
// append EOF marker
|
||||
self.push(FunctionCode::MCode(MCode::EndOfFile));
|
||||
|
||||
self.commands.serialize(writer).unwrap();
|
||||
}
|
||||
|
||||
pub fn start_region(&mut self) {
|
||||
// start a new region
|
||||
self.push(GCode::RegionMode(true));
|
||||
}
|
||||
|
||||
pub fn end_region(&mut self) {
|
||||
// end region
|
||||
self.push(GCode::RegionMode(false));
|
||||
}
|
||||
|
||||
pub fn set_polarity(&mut self, polarity: bool) {
|
||||
if polarity { // true = positive = add
|
||||
self.push(ExtendedCode::LoadPolarity(Polarity::Dark));
|
||||
} else { // false = negative = clear
|
||||
self.push(ExtendedCode::LoadPolarity(Polarity::Clear));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_polygon(&mut self, poly: &Polygon) {
|
||||
self.start_region();
|
||||
|
||||
// goto last point
|
||||
let pt = self.vertex_to_coords(poly.vertices.last().unwrap());
|
||||
self.push(gerber_types::Operation::Move(pt));
|
||||
|
||||
// now convert the full polygon
|
||||
for ref v in &poly.vertices {
|
||||
let pt = self.vertex_to_coords(&v);
|
||||
self.push(gerber_types::Operation::Interpolate(pt, None));
|
||||
}
|
||||
|
||||
self.end_region();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
#[macro_use] extern crate log;
|
||||
extern crate env_logger;
|
||||
|
||||
extern crate usvg;
|
||||
extern crate gerber_types;
|
||||
extern crate lyon;
|
||||
extern crate conv;
|
||||
|
||||
mod path_convert;
|
||||
mod gerber_builder;
|
||||
mod sort_polygons;
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::io::stdout;
|
||||
use std::env;
|
||||
|
||||
use gerber_types::*;
|
||||
|
||||
//use lyon::tessellation as tess;
|
||||
use lyon::path::iterator::PathIterator;
|
||||
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 2 && args.len() != 3 && args.len() != 4 {
|
||||
println!("Usage:\n\tsvg2gerber input.svg [output.gerb [layer_type]]");
|
||||
return;
|
||||
}
|
||||
|
||||
// load svg from disk and simplify it considerably with usvg
|
||||
let svg = usvg::Tree::from_file(&args[1], &usvg::Options::default()).expect("failed to load SVG");
|
||||
|
||||
// TODO: for a proper script we probably want to add separate parameters for all of these:
|
||||
let part_type = if args.len() < 4 {
|
||||
(gerber_types::Part::Other("Unknown".to_string()), gerber_types::FileFunction::Other("Unkown".to_string()), true)
|
||||
} else {
|
||||
match args[3].to_ascii_lowercase().as_ref() {
|
||||
"f.cu" => (gerber_types::Part::Single, gerber_types::FileFunction::Copper{layer: 1, pos: gerber_types::ExtendedPosition::Top, copper_type: None}, true),
|
||||
"b.cu" => (gerber_types::Part::Single, gerber_types::FileFunction::Copper{layer: 2, pos: gerber_types::ExtendedPosition::Bottom, copper_type: None}, true),
|
||||
"f.mask" => (gerber_types::Part::Single, gerber_types::FileFunction::Soldermask{index: None, pos: gerber_types::Position::Top}, false),
|
||||
"b.mask" => (gerber_types::Part::Single, gerber_types::FileFunction::Soldermask{index: None, pos: gerber_types::Position::Bottom}, false),
|
||||
&_ => panic!("unknown layer type {}", args[3]),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let mut gerb = gerber_builder::GerberBuilder::new(
|
||||
CoordinateFormat::new(5, 6),
|
||||
part_type.0, // part type (single PCB or 'other')
|
||||
part_type.1, // file function (copper, solder mask, ...)
|
||||
part_type.2, // polarity (true = positive = add stuff where gerber has stuff)
|
||||
);
|
||||
|
||||
//let mut fill_tess = lyon::tessellation::FillTessellator::new();
|
||||
|
||||
let mut polys = Vec::new();
|
||||
for node in svg.root().descendants() {
|
||||
if let usvg::NodeKind::Path(ref p) = *node.borrow() {
|
||||
|
||||
// TODO: do we have to handle transformations here? usvg should already have removed
|
||||
// those for us, no?
|
||||
let path = path_convert::convert_path(p).path_iter();
|
||||
|
||||
// convert path containing bezier curves and other complicated things into something
|
||||
// piece-wise linear
|
||||
let flattened = path.flattened(0.01);
|
||||
|
||||
polys.extend(sort_polygons::Polygon::from_path(flattened));
|
||||
|
||||
/*
|
||||
fill_tess.tessellate_path(
|
||||
path_convert::convert_path(p).path_iter(),
|
||||
&lyon::tessellation::FillOptions::tolerance(0.01),
|
||||
&mut data
|
||||
).expect("Error during tesselation!");
|
||||
*/
|
||||
}
|
||||
// TODO: extract layer information from Inkscape-SVG
|
||||
}
|
||||
|
||||
let mut parents = sort_polygons::create_parent_list(&polys);
|
||||
|
||||
info!("got {} polygons", polys.len());
|
||||
|
||||
parents.sort_unstable_by_key(|pi| pi.level);
|
||||
|
||||
for (idx, p) in parents.iter().enumerate() {
|
||||
info!(" - poly {} has parent {:?} and level {}", idx, p.parent_idx, p.level);
|
||||
|
||||
gerb.set_polarity(p.level % 2 == 0);
|
||||
gerb.add_polygon(p.polygon);
|
||||
}
|
||||
|
||||
if args.len() > 2 {
|
||||
if args[2] == "-" {
|
||||
// publish to stdout
|
||||
gerb.publish(&mut stdout());
|
||||
} else {
|
||||
let mut outfile = File::create(&args[2]).expect("Could not create output file");
|
||||
gerb.publish(&mut outfile);
|
||||
}
|
||||
} else {
|
||||
let path = Path::new(&args[1]);
|
||||
let path = Path::new(path.file_stem().unwrap()).with_extension("gerb");
|
||||
let mut outfile = File::create(path).expect("Could not create output file");
|
||||
gerb.publish(&mut outfile);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// from https://raw.githubusercontent.com/nical/lyon/master/examples/svg_render/src/path_convert.rs
|
||||
|
||||
use std::iter;
|
||||
use std::slice;
|
||||
|
||||
use lyon::math::Point;
|
||||
use lyon::path::PathEvent;
|
||||
use lyon::path::iterator::PathIter;
|
||||
|
||||
use usvg::{Path, PathSegment};
|
||||
|
||||
fn point(x: f64, y: f64) -> Point {
|
||||
Point::new(x as f32, y as f32)
|
||||
}
|
||||
|
||||
// Map usvg::PathSegment to lyon::path::PathEvent
|
||||
fn as_event(ps: &PathSegment) -> PathEvent {
|
||||
match *ps {
|
||||
PathSegment::MoveTo { x, y } => PathEvent::MoveTo(point(x, y)),
|
||||
PathSegment::LineTo { x, y } => PathEvent::LineTo(point(x, y)),
|
||||
PathSegment::CurveTo { x1, y1, x2, y2, x, y, } => {
|
||||
PathEvent::CubicTo(point(x1, y1), point(x2, y2), point(x, y))
|
||||
}
|
||||
PathSegment::ClosePath => PathEvent::Close,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PathConv<'a>(SegmentIter<'a>);
|
||||
|
||||
// Alias for the iterator returned by usvg::Path::iter()
|
||||
type SegmentIter<'a> = slice::Iter<'a, PathSegment>;
|
||||
|
||||
// Alias for our `interface` iterator
|
||||
type PathConvIter<'a> = iter::Map<SegmentIter<'a>, fn(&PathSegment) -> PathEvent>;
|
||||
|
||||
// Provide a function which gives back a PathIter which is compatible with
|
||||
// tesselators, so we don't have to implement the PathIterator trait
|
||||
impl<'a> PathConv<'a> {
|
||||
pub fn path_iter(self) -> PathIter<PathConvIter<'a>> {
|
||||
PathIter::new(self.0.map(as_event))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_path<'a>(p: &'a Path) -> PathConv<'a> {
|
||||
PathConv(p.segments.iter())
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
extern crate lyon;
|
||||
|
||||
//extern crate gnuplot;
|
||||
//use sort_polygons::gnuplot::AxesCommon;
|
||||
|
||||
//use lyon::tessellation as tess;
|
||||
use lyon::path::iterator::PathIterator;
|
||||
use lyon::path::FlattenedEvent;
|
||||
use lyon::algorithms::path::iterator::Flattened;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Polygon {
|
||||
pub vertices: Vec<lyon::math::Point>,
|
||||
}
|
||||
|
||||
impl Polygon {
|
||||
pub fn new() -> Self {
|
||||
Polygon {
|
||||
vertices: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_path<Iter: PathIterator>(path: Flattened<Iter>) -> Vec<Self> {
|
||||
let mut polys = Vec::new();
|
||||
let mut current_poly = None;
|
||||
|
||||
for evt in path {
|
||||
match evt {
|
||||
FlattenedEvent::MoveTo(p) => {
|
||||
let mut poly = Polygon::new();
|
||||
poly.vertices.push(p);
|
||||
current_poly = Some(poly);
|
||||
}
|
||||
|
||||
FlattenedEvent::LineTo(p) => {
|
||||
if let Some(ref mut poly) = current_poly {
|
||||
poly.vertices.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
FlattenedEvent::Close => {
|
||||
polys.push(current_poly.unwrap());
|
||||
current_poly = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
polys
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct PolyPoint<'a> {
|
||||
vertex: &'a lyon::math::Point,
|
||||
prev: &'a lyon::math::Point,
|
||||
next: &'a lyon::math::Point,
|
||||
|
||||
//pub poly_parent: &'a mut Option<&'a Polygon<'a>>,
|
||||
poly_idx: usize,
|
||||
|
||||
// TODO: equality operator that just compares 'vertex'
|
||||
}
|
||||
|
||||
impl<'a> PolyPoint<'a> {
|
||||
pub fn list(polys: &'a Vec<Polygon>) -> Vec<PolyPoint> {
|
||||
let mut pts = Vec::new();
|
||||
|
||||
for (poly_idx, poly) in polys.iter().enumerate() {
|
||||
|
||||
assert!(poly.vertices.len() >= 3, "Got a degenerate polygon with only {} vertices.", poly.vertices.len());
|
||||
|
||||
let mut v1 = &poly.vertices[poly.vertices.len()-2]; // second last element
|
||||
let mut v2 = &poly.vertices[poly.vertices.len()-1]; // last element
|
||||
|
||||
debug_assert!(v1 != v2, "got same element twice: point {}", v1);
|
||||
|
||||
for v3 in &poly.vertices {
|
||||
pts.push(PolyPoint {
|
||||
|
||||
prev: v1,
|
||||
vertex: v2,
|
||||
next: v3,
|
||||
|
||||
poly_idx: poly_idx,
|
||||
});
|
||||
|
||||
//trace!("adding Vertex: {}, {}, {}", v1, v2, v3);
|
||||
|
||||
v1 = v2;
|
||||
v2 = v3;
|
||||
}
|
||||
}
|
||||
|
||||
pts
|
||||
}
|
||||
}
|
||||
|
||||
// returns points sorted in descending y order
|
||||
pub fn sort_poly_points(pts: &mut Vec<PolyPoint>) {
|
||||
// sorting floats is only possible when we don't have NaNs
|
||||
pts.sort_by(|a, b| b.vertex.y.partial_cmp(&a.vertex.y).unwrap() );
|
||||
}
|
||||
|
||||
|
||||
struct Edge<'a> {
|
||||
upper: &'a lyon::math::Point, // higher y
|
||||
lower: &'a lyon::math::Point, // lower y
|
||||
|
||||
poly_idx: usize,
|
||||
}
|
||||
|
||||
impl<'a> Edge<'a> {
|
||||
pub fn interpolate_x(&self, y: f32) -> f32 {
|
||||
debug_assert!(self.upper.y >= y && y >= self.lower.y,
|
||||
"interpolation point must lie between edge's end points: Edge is from {} to {}, query y is {}.", self.upper.y, self.lower.y, y);
|
||||
|
||||
let r = (y - self.lower.y) / (self.upper.y - self.lower.y);
|
||||
return r * (self.upper.x - self.lower.x) + self.lower.x;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// insert or remove edge from scanline (scanline is at 'vert')
|
||||
fn handle_edge<'a>(scanline: &mut Vec<Edge<'a>>, vert: &'a lyon::math::Point, other: &'a lyon::math::Point, poly_idx: usize) {
|
||||
if vert == other {
|
||||
return; // ignore degenerate edges with zero length
|
||||
}
|
||||
//debug_assert!(vert != other, "Edge must consist of two distinct points, but got {} twice.", vert);
|
||||
trace!(" -> handling edge from {} to {} (poly {})", vert, other, poly_idx);
|
||||
|
||||
if other.y == vert.y {
|
||||
trace!(" -> ignoring horizontal edges");
|
||||
return;
|
||||
}
|
||||
|
||||
if other.y > vert.y {
|
||||
// edge ends at scanline
|
||||
// remove it from scanline
|
||||
// TODO: implement ordering trait for Edge that uses interpolated x value so we can find
|
||||
// our edge more efficiently
|
||||
trace!(" -> removing edge, it ends here");
|
||||
scanline.retain(|edge| edge.lower != vert);
|
||||
} else {
|
||||
// edge starts at scanline
|
||||
// insert it in a sorted fashion
|
||||
let index;
|
||||
match scanline.binary_search_by(|edge| {edge.interpolate_x(vert.y).partial_cmp(&vert.x).unwrap()}) {
|
||||
Ok(i) => index = i, // found other edge at this point. TODO: This should not happen and we probably want at least a warning here.
|
||||
Err(i) => index = i, // not found, but it belongs there
|
||||
}
|
||||
trace!(" -> insert edge at index {}", index);
|
||||
scanline.insert(index, Edge { upper: vert, lower: other, poly_idx: poly_idx });
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ParentInfo<'a> {
|
||||
pub polygon: &'a Polygon,
|
||||
pub parent_idx: Option<usize>,
|
||||
pub level: usize, // 0 means poly is outermost
|
||||
}
|
||||
|
||||
pub fn create_parent_list<'a>(polygons: &'a Vec<Polygon>) -> Vec<ParentInfo<'a>> {
|
||||
let mut pts = PolyPoint::list(&polygons);
|
||||
sort_poly_points(&mut pts);
|
||||
|
||||
let mut current_scanline: Vec<Edge> = Vec::new();
|
||||
let mut parents: Vec<Option<ParentInfo>> = vec![None; polygons.len()];
|
||||
|
||||
for (_step, pt) in pts.iter().enumerate() {
|
||||
trace!("scanline is at y = {}", pt.vertex.y);
|
||||
// look at edge (prev, vertex)
|
||||
handle_edge(&mut current_scanline, pt.vertex, pt.prev, pt.poly_idx);
|
||||
|
||||
// look at edge (vertex, next)
|
||||
handle_edge(&mut current_scanline, pt.vertex, pt.next, pt.poly_idx);
|
||||
|
||||
let mut parent_stack: Vec<usize> = Vec::new();
|
||||
|
||||
|
||||
|
||||
/*
|
||||
let mut fig = gnuplot::Figure::new();
|
||||
{
|
||||
let mut ax = fig.axes2d();
|
||||
|
||||
ax.set_title(&format!("Step {}", _step), &[]);
|
||||
ax.lines(&[pt.vertex.x, pt.prev.x], &[pt.vertex.y, pt.prev.y], &[gnuplot::Color("black"), gnuplot::LineWidth(2.0)]);
|
||||
ax.lines(&[pt.vertex.x, pt.next.x], &[pt.vertex.y, pt.next.y], &[gnuplot::Color("black"), gnuplot::LineWidth(2.0)]);
|
||||
ax.points(&[pt.vertex.x], &[pt.vertex.y], &[gnuplot::Color("black"), gnuplot::PointSize(5.0), gnuplot::PointSymbol('o')]);
|
||||
*/
|
||||
|
||||
// count number of edges between current vertex and the outside (while ignoring edges of
|
||||
// the current polygon)
|
||||
for ref edge in ¤t_scanline {
|
||||
|
||||
// only look at edges on the left of the current vertex
|
||||
if edge.interpolate_x(pt.vertex.y) >= pt.vertex.x {
|
||||
break;
|
||||
}
|
||||
|
||||
// ignore edges from current polygon
|
||||
if edge.poly_idx == pt.poly_idx {
|
||||
continue;
|
||||
}
|
||||
|
||||
//ax.lines(&[edge.upper.x, edge.lower.x], &[edge.upper.y, edge.lower.y], &[gnuplot::Color("red"), gnuplot::LineWidth(2.0)]);
|
||||
|
||||
// push or pop polys to/from stack
|
||||
let mut pop = false;
|
||||
if let Some(p) = parent_stack.last() {
|
||||
if *p == edge.poly_idx {
|
||||
pop = true;
|
||||
}
|
||||
}
|
||||
|
||||
if pop {
|
||||
parent_stack.pop();
|
||||
} else {
|
||||
parent_stack.push(edge.poly_idx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
}
|
||||
fig.show();
|
||||
*/
|
||||
|
||||
trace!(" -> handling point {:?}", pt.vertex);
|
||||
trace!(" -> last edge on stack of {}: {:?}", parent_stack.len(), parent_stack.last());
|
||||
|
||||
if !parents[pt.poly_idx].is_some() {
|
||||
// parent information not yet defined, add it
|
||||
parents[pt.poly_idx] = Some(ParentInfo{
|
||||
polygon: &polygons[pt.poly_idx],
|
||||
parent_idx: parent_stack.last().cloned(),
|
||||
level : parent_stack.len(),
|
||||
});
|
||||
trace!(" -> assigned parent {:?}", parent_stack.last());
|
||||
} else if let Some(ref pi) = &parents[pt.poly_idx] {
|
||||
// polygon at poly_idx already has a parent & level defined
|
||||
// make sure it is the right one
|
||||
// (this should not be necessary, just to make sure our implementation is correct
|
||||
// and our assumptions were valid)
|
||||
|
||||
assert!(pi.level == parent_stack.len(),
|
||||
"Invalid level for polygon {}: Expected {}, but we previously calculated {}.",
|
||||
pt.poly_idx, parent_stack.len(), pi.level);
|
||||
|
||||
assert!(pi.parent_idx.is_some() == parent_stack.last().is_some(),
|
||||
"Invalid parent for polygon {}: Expected to have parent? {}. Previously determined: {}.",
|
||||
pt.poly_idx, parent_stack.last().is_some(), pi.parent_idx.is_some());
|
||||
|
||||
if let Some(p) = pi.parent_idx {
|
||||
assert!(p == *parent_stack.last().unwrap(),
|
||||
"Invalid parent computed: Polygon {} already has parent {}, but we just found {:?} as parent.",
|
||||
pt.poly_idx, p, parent_stack.last());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(parents.len() == polygons.len(), "Did not process all polygons. Only got {} out of {}.", parents.len(), polygons.len());
|
||||
|
||||
parents.into_iter().map(|p| p.unwrap()).collect()
|
||||
}
|
Ładowanie…
Reference in New Issue