mirror of
https://github.com/hannobraun/Fornjot
synced 2025-10-03 06:28: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)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
builder::{CycleBuilder, FaceBuilder},
|
||||
geometry::curve::Curve,
|
||||
objects::{Cycle, Face},
|
||||
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||
services::Services,
|
||||
};
|
||||
|
||||
@ -178,20 +179,23 @@ mod tests {
|
||||
[ 1., -1.],
|
||||
];
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
exterior_points,
|
||||
&mut services,
|
||||
))
|
||||
.with_interior(CycleBuilder::polygon(
|
||||
interior_points,
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(exterior_points, &mut services)
|
||||
.insert(&mut services)
|
||||
})
|
||||
.add_interiors([Cycle::polygon(
|
||||
interior_points,
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)]);
|
||||
|
||||
let expected =
|
||||
CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]);
|
||||
assert_eq!(CurveFaceIntersection::compute(&curve, &face), expected);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -62,8 +62,9 @@ mod tests {
|
||||
|
||||
use crate::{
|
||||
algorithms::intersect::CurveFaceIntersection,
|
||||
builder::{CycleBuilder, FaceBuilder},
|
||||
geometry::curve::Curve,
|
||||
objects::{Cycle, Face},
|
||||
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||
services::Services,
|
||||
};
|
||||
|
||||
@ -85,14 +86,15 @@ mod tests {
|
||||
services.objects.surfaces.xz_plane(),
|
||||
]
|
||||
.map(|surface| {
|
||||
FaceBuilder::new(surface)
|
||||
.with_exterior(CycleBuilder::polygon(points, &mut services))
|
||||
.build(&mut services)
|
||||
Face::unbound(surface, &mut services).update_exterior(|_| {
|
||||
Cycle::polygon(points, &mut services).insert(&mut services)
|
||||
})
|
||||
});
|
||||
|
||||
let intersection = FaceFaceIntersection::compute([&a, &b]);
|
||||
|
||||
assert!(intersection.is_none());
|
||||
|
||||
services.only_validate([a, b]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -111,9 +113,9 @@ mod tests {
|
||||
services.objects.surfaces.xz_plane(),
|
||||
];
|
||||
let [a, b] = surfaces.clone().map(|surface| {
|
||||
FaceBuilder::new(surface)
|
||||
.with_exterior(CycleBuilder::polygon(points, &mut services))
|
||||
.build(&mut services)
|
||||
Face::unbound(surface, &mut services).update_exterior(|_| {
|
||||
Cycle::polygon(points, &mut services).insert(&mut services)
|
||||
})
|
||||
});
|
||||
|
||||
let intersection = FaceFaceIntersection::compute([&a, &b]);
|
||||
@ -131,5 +133,7 @@ mod tests {
|
||||
intersection_intervals: expected_intervals
|
||||
})
|
||||
);
|
||||
|
||||
services.only_validate([a, b]);
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,8 @@ mod tests {
|
||||
|
||||
use crate::{
|
||||
algorithms::intersect::{face_point::FacePointIntersection, Intersect},
|
||||
builder::{CycleBuilder, FaceBuilder},
|
||||
objects::{Cycle, Face},
|
||||
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||
services::Services,
|
||||
};
|
||||
|
||||
@ -146,28 +147,36 @@ mod tests {
|
||||
fn point_is_outside_face() {
|
||||
let mut services = Services::new();
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[0., 0.], [1., 1.], [0., 2.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[0., 0.], [1., 1.], [0., 2.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let point = Point::from([2., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
assert_eq!(intersection, None);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_vertex_while_passing_outside() {
|
||||
let mut services = Services::new();
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[0., 0.], [2., 1.], [0., 2.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[0., 0.], [2., 1.], [0., 2.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let point = Point::from([1., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
@ -175,18 +184,23 @@ mod tests {
|
||||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_vertex_at_cycle_seam() {
|
||||
let mut services = Services::new();
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[4., 2.], [0., 4.], [0., 0.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[4., 2.], [0., 4.], [0., 0.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let point = Point::from([1., 2.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
@ -194,18 +208,23 @@ mod tests {
|
||||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_vertex_while_staying_inside() {
|
||||
let mut services = Services::new();
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[0., 0.], [2., 1.], [3., 0.], [3., 4.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[0., 0.], [2., 1.], [3., 0.], [3., 4.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let point = Point::from([1., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
@ -213,18 +232,23 @@ mod tests {
|
||||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_parallel_edge_and_leaves_face_at_vertex() {
|
||||
let mut services = Services::new();
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[0., 0.], [2., 1.], [3., 1.], [0., 2.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[0., 0.], [2., 1.], [3., 1.], [0., 2.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let point = Point::from([1., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
@ -232,18 +256,23 @@ mod tests {
|
||||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_parallel_edge_and_does_not_leave_face_there() {
|
||||
let mut services = Services::new();
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[0., 0.], [2., 1.], [3., 1.], [4., 0.], [4., 5.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let point = Point::from([1., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
@ -251,18 +280,23 @@ mod tests {
|
||||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn point_is_coincident_with_edge() {
|
||||
let mut services = Services::new();
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[0., 0.], [2., 0.], [0., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[0., 0.], [2., 0.], [0., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let point = Point::from([1., 0.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
@ -276,18 +310,23 @@ mod tests {
|
||||
intersection,
|
||||
Some(FacePointIntersection::PointIsOnEdge(edge.clone()))
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn point_is_coincident_with_vertex() {
|
||||
let mut services = Services::new();
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[0., 0.], [1., 0.], [0., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[0., 0.], [1., 0.], [0., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let point = Point::from([1., 0.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
@ -304,5 +343,7 @@ mod tests {
|
||||
intersection,
|
||||
Some(FacePointIntersection::PointIsOnVertex(vertex))
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,8 @@ mod tests {
|
||||
},
|
||||
transform::TransformObject,
|
||||
},
|
||||
builder::{CycleBuilder, FaceBuilder},
|
||||
objects::{Cycle, Face},
|
||||
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||
services::Services,
|
||||
};
|
||||
|
||||
@ -162,15 +163,20 @@ mod tests {
|
||||
|
||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let face = face.translate([-1., 0., 0.], &mut services);
|
||||
|
||||
assert_eq!((&ray, &face).intersect(), None);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -179,18 +185,23 @@ mod tests {
|
||||
|
||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let face = face.translate([1., 0., 0.], &mut services);
|
||||
|
||||
assert_eq!(
|
||||
(&ray, &face).intersect(),
|
||||
Some(RayFaceIntersection::RayHitsFace)
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -199,15 +210,20 @@ mod tests {
|
||||
|
||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let face = face.translate([0., 0., 2.], &mut services);
|
||||
|
||||
assert_eq!((&ray, &face).intersect(), None);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -216,12 +232,15 @@ mod tests {
|
||||
|
||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let face = face.translate([1., 1., 0.], &mut services);
|
||||
|
||||
let edge = face
|
||||
@ -233,6 +252,8 @@ mod tests {
|
||||
(&ray, &face).intersect(),
|
||||
Some(RayFaceIntersection::RayHitsEdge(edge.clone()))
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -241,12 +262,15 @@ mod tests {
|
||||
|
||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.yz_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.yz_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let face = face.translate([1., 1., 1.], &mut services);
|
||||
|
||||
let vertex = face
|
||||
@ -261,6 +285,8 @@ mod tests {
|
||||
(&ray, &face).intersect(),
|
||||
Some(RayFaceIntersection::RayHitsVertex(vertex))
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -269,17 +295,22 @@ mod tests {
|
||||
|
||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
(&ray, &face).intersect(),
|
||||
Some(RayFaceIntersection::RayHitsFaceAndAreParallel)
|
||||
);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -288,14 +319,19 @@ mod tests {
|
||||
|
||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
});
|
||||
let face = face.translate([0., 0., 1.], &mut services);
|
||||
|
||||
assert_eq!((&ray, &face).intersect(), None);
|
||||
|
||||
services.only_validate(face);
|
||||
}
|
||||
}
|
||||
|
@ -79,8 +79,8 @@ mod tests {
|
||||
|
||||
use crate::{
|
||||
algorithms::approx::{Approx, Tolerance},
|
||||
builder::{CycleBuilder, FaceBuilder},
|
||||
objects::Face,
|
||||
objects::{Cycle, Face},
|
||||
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||
services::Services,
|
||||
};
|
||||
|
||||
@ -95,9 +95,13 @@ mod tests {
|
||||
let c = [2., 2.];
|
||||
let d = [0., 1.];
|
||||
|
||||
let face = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon([a, b, c, d], &mut services))
|
||||
.build(&mut services);
|
||||
let face =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &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 b = Point::from(b).to_xyz();
|
||||
@ -130,10 +134,14 @@ mod tests {
|
||||
|
||||
let surface = services.objects.surfaces.xy_plane();
|
||||
|
||||
let face = FaceBuilder::new(surface.clone())
|
||||
.with_exterior(CycleBuilder::polygon([a, b, c, d], &mut services))
|
||||
.with_interior(CycleBuilder::polygon([e, f, g, h], &mut services))
|
||||
.build(&mut services);
|
||||
let face = Face::unbound(surface.clone(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon([a, b, c, d], &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)?;
|
||||
|
||||
@ -188,12 +196,12 @@ mod tests {
|
||||
|
||||
let surface = services.objects.surfaces.xy_plane();
|
||||
|
||||
let face = FaceBuilder::new(surface.clone())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[a, b, c, d, e],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let face = Face::unbound(surface.clone(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon([a, b, c, d, e], &mut services)
|
||||
.insert(&mut services)
|
||||
});
|
||||
services.only_validate(&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
|
||||
|
||||
mod cycle;
|
||||
mod face;
|
||||
|
||||
pub use self::{cycle::CycleBuilder, face::FaceBuilder};
|
||||
pub use self::cycle::CycleBuilder;
|
||||
|
@ -75,6 +75,7 @@
|
||||
|
||||
mod full;
|
||||
mod object;
|
||||
mod set;
|
||||
mod stores;
|
||||
|
||||
pub use self::{
|
||||
@ -89,5 +90,6 @@ pub use self::{
|
||||
vertex::Vertex,
|
||||
},
|
||||
object::{Bare, BehindHandle, Form, Object, WithHandle},
|
||||
set::ObjectSet,
|
||||
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,
|
||||
};
|
||||
|
||||
use super::{BuildHalfEdge, BuildSurface};
|
||||
use super::{BuildCycle, BuildHalfEdge, BuildSurface};
|
||||
|
||||
/// Build a [`Face`]
|
||||
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
|
||||
fn triangle(
|
||||
points: [impl Into<Point<3>>; 3],
|
||||
|
@ -10,6 +10,12 @@ pub trait UpdateFace {
|
||||
&self,
|
||||
f: impl FnOnce(&Handle<Cycle>) -> Handle<Cycle>,
|
||||
) -> Self;
|
||||
|
||||
/// Add the provides interiors to the face
|
||||
fn add_interiors(
|
||||
&self,
|
||||
interiors: impl IntoIterator<Item = Handle<Cycle>>,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
impl UpdateFace for Face {
|
||||
@ -26,4 +32,18 @@ impl UpdateFace for Face {
|
||||
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 validation;
|
||||
|
||||
use crate::objects::{Object, Objects, WithHandle};
|
||||
use crate::objects::{Object, ObjectSet, Objects, WithHandle};
|
||||
|
||||
pub use self::{
|
||||
objects::{InsertObject, Operation},
|
||||
@ -52,6 +52,15 @@ impl Services {
|
||||
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 {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{collections::BTreeMap, thread};
|
||||
|
||||
use crate::{
|
||||
objects::{BehindHandle, Object},
|
||||
objects::{BehindHandle, Object, ObjectSet},
|
||||
storage::ObjectId,
|
||||
validate::ValidationError,
|
||||
};
|
||||
@ -39,16 +39,33 @@ impl State for Validation {
|
||||
type Event = ValidationEvent;
|
||||
|
||||
fn decide(&self, command: Self::Command, events: &mut Vec<Self::Event>) {
|
||||
let ValidationCommand::ValidateObject { object } = command;
|
||||
|
||||
let mut errors = Vec::new();
|
||||
object.validate(&mut errors);
|
||||
|
||||
for err in errors {
|
||||
events.push(ValidationEvent::ValidationFailed {
|
||||
object: object.clone(),
|
||||
err,
|
||||
});
|
||||
match command {
|
||||
ValidationCommand::ValidateObject { object } => {
|
||||
object.validate(&mut errors);
|
||||
|
||||
for err in errors {
|
||||
events.push(ValidationEvent::ValidationFailed {
|
||||
object: object.clone(),
|
||||
err,
|
||||
});
|
||||
}
|
||||
}
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +74,7 @@ impl State for Validation {
|
||||
ValidationEvent::ValidationFailed { object, err } => {
|
||||
self.errors.insert(object.id(), err.clone());
|
||||
}
|
||||
ValidationEvent::ClearErrors => self.errors.clear(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,6 +86,12 @@ pub enum ValidationCommand {
|
||||
/// The object to validate
|
||||
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
|
||||
@ -81,4 +105,7 @@ pub enum ValidationEvent {
|
||||
/// The validation error
|
||||
err: ValidationError,
|
||||
},
|
||||
|
||||
/// All stored validation errors are being cleared
|
||||
ClearErrors,
|
||||
}
|
||||
|
@ -73,8 +73,8 @@ mod tests {
|
||||
use crate::{
|
||||
algorithms::reverse::Reverse,
|
||||
assert_contains_err,
|
||||
builder::{CycleBuilder, FaceBuilder},
|
||||
objects::Face,
|
||||
objects::{Cycle, Face},
|
||||
operations::{BuildCycle, BuildFace, Insert, UpdateFace},
|
||||
services::Services,
|
||||
validate::{FaceValidationError, Validate, ValidationError},
|
||||
};
|
||||
@ -83,16 +83,20 @@ mod tests {
|
||||
fn face_invalid_interior_winding() -> anyhow::Result<()> {
|
||||
let mut services = Services::new();
|
||||
|
||||
let valid = FaceBuilder::new(services.objects.surfaces.xy_plane())
|
||||
.with_exterior(CycleBuilder::polygon(
|
||||
[[0., 0.], [3., 0.], [0., 3.]],
|
||||
&mut services,
|
||||
))
|
||||
.with_interior(CycleBuilder::polygon(
|
||||
[[1., 1.], [1., 2.], [2., 1.]],
|
||||
&mut services,
|
||||
))
|
||||
.build(&mut services);
|
||||
let valid =
|
||||
Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
|
||||
.update_exterior(|_| {
|
||||
Cycle::polygon(
|
||||
[[0., 0.], [3., 0.], [0., 3.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)
|
||||
})
|
||||
.add_interiors([Cycle::polygon(
|
||||
[[1., 1.], [1., 2.], [2., 1.]],
|
||||
&mut services,
|
||||
)
|
||||
.insert(&mut services)]);
|
||||
let invalid = {
|
||||
let interiors = valid
|
||||
.interiors()
|
||||
@ -116,6 +120,8 @@ mod tests {
|
||||
)
|
||||
);
|
||||
|
||||
services.only_validate(valid);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user