mirror of
https://github.com/hannobraun/Fornjot
synced 2025-10-03 22:48:11 +00:00
Merge pull request #1812 from hannobraun/builder
Migrate tests away from `FaceBuilder`, towards operations API
This commit is contained in:
commit
fa02019d44
@ -150,8 +150,9 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
builder::{CycleBuilder, FaceBuilder},
|
|
||||||
geometry::curve::Curve,
|
geometry::curve::Curve,
|
||||||
|
objects::{Cycle, Face},
|
||||||
|
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,20 +179,23 @@ mod tests {
|
|||||||
[ 1., -1.],
|
[ 1., -1.],
|
||||||
];
|
];
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
exterior_points,
|
.update_exterior(|_| {
|
||||||
&mut services,
|
Cycle::polygon(exterior_points, &mut services)
|
||||||
))
|
.insert(&mut services)
|
||||||
.with_interior(CycleBuilder::polygon(
|
})
|
||||||
|
.add_interiors([Cycle::polygon(
|
||||||
interior_points,
|
interior_points,
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)]);
|
||||||
|
|
||||||
let expected =
|
let expected =
|
||||||
CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]);
|
CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]);
|
||||||
assert_eq!(CurveFaceIntersection::compute(&curve, &face), expected);
|
assert_eq!(CurveFaceIntersection::compute(&curve, &face), expected);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -62,8 +62,9 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
algorithms::intersect::CurveFaceIntersection,
|
algorithms::intersect::CurveFaceIntersection,
|
||||||
builder::{CycleBuilder, FaceBuilder},
|
|
||||||
geometry::curve::Curve,
|
geometry::curve::Curve,
|
||||||
|
objects::{Cycle, Face},
|
||||||
|
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -85,14 +86,15 @@ mod tests {
|
|||||||
services.objects.surfaces.xz_plane(),
|
services.objects.surfaces.xz_plane(),
|
||||||
]
|
]
|
||||||
.map(|surface| {
|
.map(|surface| {
|
||||||
FaceBuilder::new(surface)
|
Face::unbound(surface, &mut services).update_exterior(|_| {
|
||||||
.with_exterior(CycleBuilder::polygon(points, &mut services))
|
Cycle::polygon(points, &mut services).insert(&mut services)
|
||||||
.build(&mut services)
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let intersection = FaceFaceIntersection::compute([&a, &b]);
|
let intersection = FaceFaceIntersection::compute([&a, &b]);
|
||||||
|
|
||||||
assert!(intersection.is_none());
|
assert!(intersection.is_none());
|
||||||
|
|
||||||
|
services.only_validate([a, b]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -111,9 +113,9 @@ mod tests {
|
|||||||
services.objects.surfaces.xz_plane(),
|
services.objects.surfaces.xz_plane(),
|
||||||
];
|
];
|
||||||
let [a, b] = surfaces.clone().map(|surface| {
|
let [a, b] = surfaces.clone().map(|surface| {
|
||||||
FaceBuilder::new(surface)
|
Face::unbound(surface, &mut services).update_exterior(|_| {
|
||||||
.with_exterior(CycleBuilder::polygon(points, &mut services))
|
Cycle::polygon(points, &mut services).insert(&mut services)
|
||||||
.build(&mut services)
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let intersection = FaceFaceIntersection::compute([&a, &b]);
|
let intersection = FaceFaceIntersection::compute([&a, &b]);
|
||||||
@ -131,5 +133,7 @@ mod tests {
|
|||||||
intersection_intervals: expected_intervals
|
intersection_intervals: expected_intervals
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate([a, b]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,8 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
algorithms::intersect::{face_point::FacePointIntersection, Intersect},
|
algorithms::intersect::{face_point::FacePointIntersection, Intersect},
|
||||||
builder::{CycleBuilder, FaceBuilder},
|
objects::{Cycle, Face},
|
||||||
|
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -146,28 +147,36 @@ mod tests {
|
|||||||
fn point_is_outside_face() {
|
fn point_is_outside_face() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[0., 0.], [1., 1.], [0., 2.]],
|
[[0., 0.], [1., 1.], [0., 2.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let point = Point::from([2., 1.]);
|
let point = Point::from([2., 1.]);
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
let intersection = (&face, &point).intersect();
|
||||||
assert_eq!(intersection, None);
|
assert_eq!(intersection, None);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ray_hits_vertex_while_passing_outside() {
|
fn ray_hits_vertex_while_passing_outside() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[0., 0.], [2., 1.], [0., 2.]],
|
[[0., 0.], [2., 1.], [0., 2.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let point = Point::from([1., 1.]);
|
let point = Point::from([1., 1.]);
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
let intersection = (&face, &point).intersect();
|
||||||
@ -175,18 +184,23 @@ mod tests {
|
|||||||
intersection,
|
intersection,
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
Some(FacePointIntersection::PointIsInsideFace)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ray_hits_vertex_at_cycle_seam() {
|
fn ray_hits_vertex_at_cycle_seam() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[4., 2.], [0., 4.], [0., 0.]],
|
[[4., 2.], [0., 4.], [0., 0.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let point = Point::from([1., 2.]);
|
let point = Point::from([1., 2.]);
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
let intersection = (&face, &point).intersect();
|
||||||
@ -194,18 +208,23 @@ mod tests {
|
|||||||
intersection,
|
intersection,
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
Some(FacePointIntersection::PointIsInsideFace)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ray_hits_vertex_while_staying_inside() {
|
fn ray_hits_vertex_while_staying_inside() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[0., 0.], [2., 1.], [3., 0.], [3., 4.]],
|
[[0., 0.], [2., 1.], [3., 0.], [3., 4.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let point = Point::from([1., 1.]);
|
let point = Point::from([1., 1.]);
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
let intersection = (&face, &point).intersect();
|
||||||
@ -213,18 +232,23 @@ mod tests {
|
|||||||
intersection,
|
intersection,
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
Some(FacePointIntersection::PointIsInsideFace)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ray_hits_parallel_edge_and_leaves_face_at_vertex() {
|
fn ray_hits_parallel_edge_and_leaves_face_at_vertex() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[0., 0.], [2., 1.], [3., 1.], [0., 2.]],
|
[[0., 0.], [2., 1.], [3., 1.], [0., 2.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let point = Point::from([1., 1.]);
|
let point = Point::from([1., 1.]);
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
let intersection = (&face, &point).intersect();
|
||||||
@ -232,18 +256,23 @@ mod tests {
|
|||||||
intersection,
|
intersection,
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
Some(FacePointIntersection::PointIsInsideFace)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ray_hits_parallel_edge_and_does_not_leave_face_there() {
|
fn ray_hits_parallel_edge_and_does_not_leave_face_there() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.]],
|
[[0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let point = Point::from([1., 1.]);
|
let point = Point::from([1., 1.]);
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
let intersection = (&face, &point).intersect();
|
||||||
@ -251,18 +280,23 @@ mod tests {
|
|||||||
intersection,
|
intersection,
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
Some(FacePointIntersection::PointIsInsideFace)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn point_is_coincident_with_edge() {
|
fn point_is_coincident_with_edge() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[0., 0.], [2., 0.], [0., 1.]],
|
[[0., 0.], [2., 0.], [0., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let point = Point::from([1., 0.]);
|
let point = Point::from([1., 0.]);
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
let intersection = (&face, &point).intersect();
|
||||||
@ -276,18 +310,23 @@ mod tests {
|
|||||||
intersection,
|
intersection,
|
||||||
Some(FacePointIntersection::PointIsOnEdge(edge.clone()))
|
Some(FacePointIntersection::PointIsOnEdge(edge.clone()))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn point_is_coincident_with_vertex() {
|
fn point_is_coincident_with_vertex() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[0., 0.], [1., 0.], [0., 1.]],
|
[[0., 0.], [1., 0.], [0., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let point = Point::from([1., 0.]);
|
let point = Point::from([1., 0.]);
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
let intersection = (&face, &point).intersect();
|
||||||
@ -304,5 +343,7 @@ mod tests {
|
|||||||
intersection,
|
intersection,
|
||||||
Some(FacePointIntersection::PointIsOnVertex(vertex))
|
Some(FacePointIntersection::PointIsOnVertex(vertex))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,8 @@ mod tests {
|
|||||||
},
|
},
|
||||||
transform::TransformObject,
|
transform::TransformObject,
|
||||||
},
|
},
|
||||||
builder::{CycleBuilder, FaceBuilder},
|
objects::{Cycle, Face},
|
||||||
|
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -162,15 +163,20 @@ mod tests {
|
|||||||
|
|
||||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let face = face.translate([-1., 0., 0.], &mut services);
|
let face = face.translate([-1., 0., 0.], &mut services);
|
||||||
|
|
||||||
assert_eq!((&ray, &face).intersect(), None);
|
assert_eq!((&ray, &face).intersect(), None);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -179,18 +185,23 @@ mod tests {
|
|||||||
|
|
||||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let face = face.translate([1., 0., 0.], &mut services);
|
let face = face.translate([1., 0., 0.], &mut services);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&ray, &face).intersect(),
|
(&ray, &face).intersect(),
|
||||||
Some(RayFaceIntersection::RayHitsFace)
|
Some(RayFaceIntersection::RayHitsFace)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -199,15 +210,20 @@ mod tests {
|
|||||||
|
|
||||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let face = face.translate([0., 0., 2.], &mut services);
|
let face = face.translate([0., 0., 2.], &mut services);
|
||||||
|
|
||||||
assert_eq!((&ray, &face).intersect(), None);
|
assert_eq!((&ray, &face).intersect(), None);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -216,12 +232,15 @@ mod tests {
|
|||||||
|
|
||||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let face = face.translate([1., 1., 0.], &mut services);
|
let face = face.translate([1., 1., 0.], &mut services);
|
||||||
|
|
||||||
let edge = face
|
let edge = face
|
||||||
@ -233,6 +252,8 @@ mod tests {
|
|||||||
(&ray, &face).intersect(),
|
(&ray, &face).intersect(),
|
||||||
Some(RayFaceIntersection::RayHitsEdge(edge.clone()))
|
Some(RayFaceIntersection::RayHitsEdge(edge.clone()))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -241,12 +262,15 @@ mod tests {
|
|||||||
|
|
||||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let face = face.translate([1., 1., 1.], &mut services);
|
let face = face.translate([1., 1., 1.], &mut services);
|
||||||
|
|
||||||
let vertex = face
|
let vertex = face
|
||||||
@ -261,6 +285,8 @@ mod tests {
|
|||||||
(&ray, &face).intersect(),
|
(&ray, &face).intersect(),
|
||||||
Some(RayFaceIntersection::RayHitsVertex(vertex))
|
Some(RayFaceIntersection::RayHitsVertex(vertex))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -269,17 +295,22 @@ mod tests {
|
|||||||
|
|
||||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&ray, &face).intersect(),
|
(&ray, &face).intersect(),
|
||||||
Some(RayFaceIntersection::RayHitsFaceAndAreParallel)
|
Some(RayFaceIntersection::RayHitsFaceAndAreParallel)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -288,14 +319,19 @@ mod tests {
|
|||||||
|
|
||||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
});
|
||||||
let face = face.translate([0., 0., 1.], &mut services);
|
let face = face.translate([0., 0., 1.], &mut services);
|
||||||
|
|
||||||
assert_eq!((&ray, &face).intersect(), None);
|
assert_eq!((&ray, &face).intersect(), None);
|
||||||
|
|
||||||
|
services.only_validate(face);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,8 +79,8 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
algorithms::approx::{Approx, Tolerance},
|
algorithms::approx::{Approx, Tolerance},
|
||||||
builder::{CycleBuilder, FaceBuilder},
|
objects::{Cycle, Face},
|
||||||
objects::Face,
|
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -95,9 +95,13 @@ mod tests {
|
|||||||
let c = [2., 2.];
|
let c = [2., 2.];
|
||||||
let d = [0., 1.];
|
let d = [0., 1.];
|
||||||
|
|
||||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let face =
|
||||||
.with_exterior(CycleBuilder::polygon([a, b, c, d], &mut services))
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
.build(&mut services);
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon([a, b, c, d], &mut services)
|
||||||
|
.insert(&mut services)
|
||||||
|
});
|
||||||
|
services.only_validate(&face);
|
||||||
|
|
||||||
let a = Point::from(a).to_xyz();
|
let a = Point::from(a).to_xyz();
|
||||||
let b = Point::from(b).to_xyz();
|
let b = Point::from(b).to_xyz();
|
||||||
@ -130,10 +134,14 @@ mod tests {
|
|||||||
|
|
||||||
let surface = services.objects.surfaces.xy_plane();
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
|
|
||||||
let face = FaceBuilder::new(surface.clone())
|
let face = Face::unbound(surface.clone(), &mut services)
|
||||||
.with_exterior(CycleBuilder::polygon([a, b, c, d], &mut services))
|
.update_exterior(|_| {
|
||||||
.with_interior(CycleBuilder::polygon([e, f, g, h], &mut services))
|
Cycle::polygon([a, b, c, d], &mut services)
|
||||||
.build(&mut services);
|
.insert(&mut services)
|
||||||
|
})
|
||||||
|
.add_interiors([Cycle::polygon([e, f, g, h], &mut services)
|
||||||
|
.insert(&mut services)]);
|
||||||
|
services.only_validate(&face);
|
||||||
|
|
||||||
let triangles = triangulate(face)?;
|
let triangles = triangulate(face)?;
|
||||||
|
|
||||||
@ -188,12 +196,12 @@ mod tests {
|
|||||||
|
|
||||||
let surface = services.objects.surfaces.xy_plane();
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
|
|
||||||
let face = FaceBuilder::new(surface.clone())
|
let face = Face::unbound(surface.clone(), &mut services)
|
||||||
.with_exterior(CycleBuilder::polygon(
|
.update_exterior(|_| {
|
||||||
[a, b, c, d, e],
|
Cycle::polygon([a, b, c, d, e], &mut services)
|
||||||
&mut services,
|
.insert(&mut services)
|
||||||
))
|
});
|
||||||
.build(&mut services);
|
services.only_validate(&face);
|
||||||
|
|
||||||
let triangles = triangulate(face)?;
|
let triangles = triangulate(face)?;
|
||||||
|
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
use fj_interop::mesh::Color;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
objects::{Face, Surface},
|
|
||||||
operations::Insert,
|
|
||||||
services::Services,
|
|
||||||
storage::Handle,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::CycleBuilder;
|
|
||||||
|
|
||||||
/// Builder API for [`Face`]
|
|
||||||
pub struct FaceBuilder {
|
|
||||||
surface: Handle<Surface>,
|
|
||||||
exterior: CycleBuilder,
|
|
||||||
interiors: Vec<CycleBuilder>,
|
|
||||||
color: Option<Color>,
|
|
||||||
}
|
|
||||||
impl FaceBuilder {
|
|
||||||
/// Create an instance of `FaceBuilder`
|
|
||||||
pub fn new(surface: Handle<Surface>) -> Self {
|
|
||||||
Self {
|
|
||||||
surface,
|
|
||||||
exterior: CycleBuilder::new(),
|
|
||||||
interiors: Vec::new(),
|
|
||||||
color: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replace the face's exterior cycle
|
|
||||||
pub fn with_exterior(mut self, exterior: CycleBuilder) -> Self {
|
|
||||||
self.exterior = exterior;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add an interior cycle to the face
|
|
||||||
pub fn with_interior(mut self, interior: CycleBuilder) -> Self {
|
|
||||||
self.interiors.push(interior);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Define the color of the face
|
|
||||||
pub fn with_color(mut self, color: Color) -> Self {
|
|
||||||
self.color = Some(color);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build the face
|
|
||||||
pub fn build(self, services: &mut Services) -> Face {
|
|
||||||
let exterior = self.exterior.build(services).insert(services);
|
|
||||||
let interiors = self
|
|
||||||
.interiors
|
|
||||||
.into_iter()
|
|
||||||
.map(|cycle| cycle.build(services).insert(services));
|
|
||||||
|
|
||||||
Face::new(self.surface, exterior, interiors, self.color)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
//! API for building objects
|
//! API for building objects
|
||||||
|
|
||||||
mod cycle;
|
mod cycle;
|
||||||
mod face;
|
|
||||||
|
|
||||||
pub use self::{cycle::CycleBuilder, face::FaceBuilder};
|
pub use self::cycle::CycleBuilder;
|
||||||
|
@ -75,6 +75,7 @@
|
|||||||
|
|
||||||
mod full;
|
mod full;
|
||||||
mod object;
|
mod object;
|
||||||
|
mod set;
|
||||||
mod stores;
|
mod stores;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@ -89,5 +90,6 @@ pub use self::{
|
|||||||
vertex::Vertex,
|
vertex::Vertex,
|
||||||
},
|
},
|
||||||
object::{Bare, BehindHandle, Form, Object, WithHandle},
|
object::{Bare, BehindHandle, Form, Object, WithHandle},
|
||||||
|
set::ObjectSet,
|
||||||
stores::{Objects, Surfaces},
|
stores::{Objects, Surfaces},
|
||||||
};
|
};
|
||||||
|
106
crates/fj-kernel/src/objects/set.rs
Normal file
106
crates/fj-kernel/src/objects/set.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
use std::collections::{btree_set, BTreeSet};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
BehindHandle, Cycle, Face, GlobalEdge, HalfEdge, Object, Surface, Vertex,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A graph of objects and their relationships
|
||||||
|
pub struct ObjectSet {
|
||||||
|
inner: BTreeSet<Object<BehindHandle>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Face> for ObjectSet {
|
||||||
|
fn from(face: &Face) -> Self {
|
||||||
|
let mut self_ = Self {
|
||||||
|
inner: BTreeSet::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
face.insert_into_set(&mut self_);
|
||||||
|
|
||||||
|
self_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Face> for ObjectSet {
|
||||||
|
fn from(face: Face) -> Self {
|
||||||
|
Self::from(&face)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Faces> From<Faces> for ObjectSet
|
||||||
|
where
|
||||||
|
Faces: IntoIterator<Item = Face>,
|
||||||
|
{
|
||||||
|
fn from(faces: Faces) -> Self {
|
||||||
|
let mut self_ = Self {
|
||||||
|
inner: BTreeSet::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for face in faces {
|
||||||
|
face.insert_into_set(&mut self_);
|
||||||
|
}
|
||||||
|
|
||||||
|
self_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for ObjectSet {
|
||||||
|
type Item = Object<BehindHandle>;
|
||||||
|
type IntoIter = btree_set::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.inner.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait InsertIntoSet {
|
||||||
|
fn insert_into_set(&self, objects: &mut ObjectSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InsertIntoSet for Cycle {
|
||||||
|
fn insert_into_set(&self, objects: &mut ObjectSet) {
|
||||||
|
for half_edge in self.half_edges() {
|
||||||
|
objects.inner.insert(half_edge.clone().into());
|
||||||
|
half_edge.insert_into_set(objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InsertIntoSet for Face {
|
||||||
|
fn insert_into_set(&self, objects: &mut ObjectSet) {
|
||||||
|
objects.inner.insert(self.surface().clone().into());
|
||||||
|
self.surface().insert_into_set(objects);
|
||||||
|
|
||||||
|
objects.inner.insert(self.exterior().clone().into());
|
||||||
|
self.exterior().insert_into_set(objects);
|
||||||
|
|
||||||
|
for interior in self.interiors() {
|
||||||
|
objects.inner.insert(interior.clone().into());
|
||||||
|
}
|
||||||
|
for interior in self.interiors() {
|
||||||
|
interior.insert_into_set(objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InsertIntoSet for GlobalEdge {
|
||||||
|
fn insert_into_set(&self, _: &mut ObjectSet) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InsertIntoSet for HalfEdge {
|
||||||
|
fn insert_into_set(&self, objects: &mut ObjectSet) {
|
||||||
|
objects.inner.insert(self.start_vertex().clone().into());
|
||||||
|
self.start_vertex().insert_into_set(objects);
|
||||||
|
|
||||||
|
objects.inner.insert(self.global_form().clone().into());
|
||||||
|
self.global_form().insert_into_set(objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InsertIntoSet for Surface {
|
||||||
|
fn insert_into_set(&self, _: &mut ObjectSet) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InsertIntoSet for Vertex {
|
||||||
|
fn insert_into_set(&self, _: &mut ObjectSet) {}
|
||||||
|
}
|
@ -8,10 +8,16 @@ use crate::{
|
|||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{BuildHalfEdge, BuildSurface};
|
use super::{BuildCycle, BuildHalfEdge, BuildSurface};
|
||||||
|
|
||||||
/// Build a [`Face`]
|
/// Build a [`Face`]
|
||||||
pub trait BuildFace {
|
pub trait BuildFace {
|
||||||
|
/// Build a face with an empty exterior, no interiors, and no color
|
||||||
|
fn unbound(surface: Handle<Surface>, services: &mut Services) -> Face {
|
||||||
|
let exterior = Cycle::empty().insert(services);
|
||||||
|
Face::new(surface, exterior, [], None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Build a triangle
|
/// Build a triangle
|
||||||
fn triangle(
|
fn triangle(
|
||||||
points: [impl Into<Point<3>>; 3],
|
points: [impl Into<Point<3>>; 3],
|
||||||
|
@ -10,6 +10,12 @@ pub trait UpdateFace {
|
|||||||
&self,
|
&self,
|
||||||
f: impl FnOnce(&Handle<Cycle>) -> Handle<Cycle>,
|
f: impl FnOnce(&Handle<Cycle>) -> Handle<Cycle>,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
|
|
||||||
|
/// Add the provides interiors to the face
|
||||||
|
fn add_interiors(
|
||||||
|
&self,
|
||||||
|
interiors: impl IntoIterator<Item = Handle<Cycle>>,
|
||||||
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateFace for Face {
|
impl UpdateFace for Face {
|
||||||
@ -26,4 +32,18 @@ impl UpdateFace for Face {
|
|||||||
self.color(),
|
self.color(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_interiors(
|
||||||
|
&self,
|
||||||
|
interiors: impl IntoIterator<Item = Handle<Cycle>>,
|
||||||
|
) -> Self {
|
||||||
|
let interiors = self.interiors().cloned().chain(interiors);
|
||||||
|
|
||||||
|
Face::new(
|
||||||
|
self.surface().clone(),
|
||||||
|
self.exterior().clone(),
|
||||||
|
interiors,
|
||||||
|
self.color(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ mod objects;
|
|||||||
mod service;
|
mod service;
|
||||||
mod validation;
|
mod validation;
|
||||||
|
|
||||||
use crate::objects::{Object, Objects, WithHandle};
|
use crate::objects::{Object, ObjectSet, Objects, WithHandle};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
objects::{InsertObject, Operation},
|
objects::{InsertObject, Operation},
|
||||||
@ -52,6 +52,15 @@ impl Services {
|
|||||||
self.validation.execute(command, &mut Vec::new());
|
self.validation.execute(command, &mut Vec::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validate the provided objects and forget all other validation errors
|
||||||
|
pub fn only_validate(&mut self, objects: impl Into<ObjectSet>) {
|
||||||
|
let objects = objects.into();
|
||||||
|
|
||||||
|
let mut events = Vec::new();
|
||||||
|
self.validation
|
||||||
|
.execute(ValidationCommand::OnlyValidate { objects }, &mut events);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Services {
|
impl Default for Services {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{collections::BTreeMap, thread};
|
use std::{collections::BTreeMap, thread};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{BehindHandle, Object},
|
objects::{BehindHandle, Object, ObjectSet},
|
||||||
storage::ObjectId,
|
storage::ObjectId,
|
||||||
validate::ValidationError,
|
validate::ValidationError,
|
||||||
};
|
};
|
||||||
@ -39,9 +39,10 @@ impl State for Validation {
|
|||||||
type Event = ValidationEvent;
|
type Event = ValidationEvent;
|
||||||
|
|
||||||
fn decide(&self, command: Self::Command, events: &mut Vec<Self::Event>) {
|
fn decide(&self, command: Self::Command, events: &mut Vec<Self::Event>) {
|
||||||
let ValidationCommand::ValidateObject { object } = command;
|
|
||||||
|
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
match command {
|
||||||
|
ValidationCommand::ValidateObject { object } => {
|
||||||
object.validate(&mut errors);
|
object.validate(&mut errors);
|
||||||
|
|
||||||
for err in errors {
|
for err in errors {
|
||||||
@ -51,12 +52,29 @@ impl State for Validation {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ValidationCommand::OnlyValidate { objects } => {
|
||||||
|
events.push(ValidationEvent::ClearErrors);
|
||||||
|
|
||||||
|
for object in objects {
|
||||||
|
object.validate(&mut errors);
|
||||||
|
|
||||||
|
for err in errors.drain(..) {
|
||||||
|
events.push(ValidationEvent::ValidationFailed {
|
||||||
|
object: object.clone(),
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn evolve(&mut self, event: &Self::Event) {
|
fn evolve(&mut self, event: &Self::Event) {
|
||||||
match event {
|
match event {
|
||||||
ValidationEvent::ValidationFailed { object, err } => {
|
ValidationEvent::ValidationFailed { object, err } => {
|
||||||
self.errors.insert(object.id(), err.clone());
|
self.errors.insert(object.id(), err.clone());
|
||||||
}
|
}
|
||||||
|
ValidationEvent::ClearErrors => self.errors.clear(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,6 +86,12 @@ pub enum ValidationCommand {
|
|||||||
/// The object to validate
|
/// The object to validate
|
||||||
object: Object<BehindHandle>,
|
object: Object<BehindHandle>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Validate the provided objects, discard all other validation errors
|
||||||
|
OnlyValidate {
|
||||||
|
/// The objects to validate
|
||||||
|
objects: ObjectSet,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The event produced by the validation service
|
/// The event produced by the validation service
|
||||||
@ -81,4 +105,7 @@ pub enum ValidationEvent {
|
|||||||
/// The validation error
|
/// The validation error
|
||||||
err: ValidationError,
|
err: ValidationError,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// All stored validation errors are being cleared
|
||||||
|
ClearErrors,
|
||||||
}
|
}
|
||||||
|
@ -73,8 +73,8 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
algorithms::reverse::Reverse,
|
algorithms::reverse::Reverse,
|
||||||
assert_contains_err,
|
assert_contains_err,
|
||||||
builder::{CycleBuilder, FaceBuilder},
|
objects::{Cycle, Face},
|
||||||
objects::Face,
|
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||||
services::Services,
|
services::Services,
|
||||||
validate::{FaceValidationError, Validate, ValidationError},
|
validate::{FaceValidationError, Validate, ValidationError},
|
||||||
};
|
};
|
||||||
@ -83,16 +83,20 @@ mod tests {
|
|||||||
fn face_invalid_interior_winding() -> anyhow::Result<()> {
|
fn face_invalid_interior_winding() -> anyhow::Result<()> {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let valid = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
let valid =
|
||||||
.with_exterior(CycleBuilder::polygon(
|
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||||
|
.update_exterior(|_| {
|
||||||
|
Cycle::polygon(
|
||||||
[[0., 0.], [3., 0.], [0., 3.]],
|
[[0., 0.], [3., 0.], [0., 3.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.with_interior(CycleBuilder::polygon(
|
.insert(&mut services)
|
||||||
|
})
|
||||||
|
.add_interiors([Cycle::polygon(
|
||||||
[[1., 1.], [1., 2.], [2., 1.]],
|
[[1., 1.], [1., 2.], [2., 1.]],
|
||||||
&mut services,
|
&mut services,
|
||||||
))
|
)
|
||||||
.build(&mut services);
|
.insert(&mut services)]);
|
||||||
let invalid = {
|
let invalid = {
|
||||||
let interiors = valid
|
let interiors = valid
|
||||||
.interiors()
|
.interiors()
|
||||||
@ -116,6 +120,8 @@ mod tests {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.only_validate(valid);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user