5 #include <cgv/math/fvec.h>
6 #include <cgv/media/axis_aligned_box.h>
7 #include <cgv/media/color.h>
9 #include "../lib_begin.h"
15 class CGV_API convex_polyhedron_base
19 enum VertexPlaneLocation {
25 enum FacePlaneLocation {
30 FPL_TOUCH_NOTHING = 0,
36 FPL_SPLIT_TWO_VERTICES,
37 FPL_SPLIT_VERTEX_EDGE,
41 FPL_TWO_NON_ADJACENT_VERTICES_TOUCH,
42 FPL_INCOMLETE_TOUCH_FACE,
45 FPL_ODD_TRANSITION_NUMBER,
47 FPL_UNCONSIDERED_CASE,
49 FPL_FIRST_INVALID_CASE = FPL_TWO_NON_ADJACENT_VERTICES_TOUCH
53 typedef std::vector<int> face_type;
56 typedef std::vector<face_type> shell_type;
60 std::vector<shell_type> shells;
63 static int to_index(VertexPlaneLocation vertex_location) {
return (
int)vertex_location + 1; }
65 static bool is_face_location_on_one_side(FacePlaneLocation face_location) {
return face_location < FPL_TOUCH_FACE; }
67 static bool is_face_location_split(FacePlaneLocation face_location) {
return face_location > FPL_TOUCH_FACE && face_location < FPL_FIRST_INVALID_CASE; }
69 static bool is_valid_face_location(FacePlaneLocation face_location) {
return face_location < FPL_FIRST_INVALID_CASE; }
71 static int box_get_face_vertex_index(
int fi,
int ci);
73 static void change_orientation(face_type& face);
75 static VertexPlaneLocation vertex_location_side(
const std::vector<VertexPlaneLocation>& vertex_locations);
77 static VertexPlaneLocation shell_location_side(
const std::vector<FacePlaneLocation>& face_locations);
80 static std::pair<int, int> extract_touching_halfedge(
const face_type& face,
const std::vector<VertexPlaneLocation>& vertex_locations);
82 FacePlaneLocation get_face_plane_location(
const face_type& face,
const std::vector<VertexPlaneLocation>& vertex_locations)
const;
85 size_t get_nr_shells()
const {
return shells.size(); }
88 const shell_type& shell(
int si)
const {
return shells[si]; }
90 shell_type& shell(
int si) {
return shells[si]; }
92 virtual int add_shell() { shells.push_back(shell_type());
return (
int)shells.size() - 1; }
94 virtual void del_shell(
int si) { shells.erase(shells.begin() + si); }
96 virtual void copy_shell(
int si_source,
int si_target) { shell(si_target) = shell(si_source); }
98 virtual void swap_shells(
int si0,
int si1) { std::swap(shell(si0), shell(si1)); }
100 size_t get_nr_faces(
int si = 0)
const {
return shell(si).size(); }
102 virtual int add_face(
int si = 0) { shell(si).push_back(face_type());
return (
int)shell(si).size() - 1; }
104 virtual void del_face(
int fi,
int si = 0) { shell(si).erase(shell(si).begin() + fi); }
106 const face_type& face(
int fi,
int si = 0)
const {
return shell(si)[fi]; }
108 face_type& face(
int fi,
int si = 0) {
return shell(si)[fi]; }
112 virtual size_t get_nr_vertices()
const = 0;
115 virtual void del_vertex(
int vi) = 0;
117 void remove_redundant_vertices();
123 template <
typename T, cgv::type::
int32_type TCDim = 2>
153 size_t get_nr_vertices()
const {
return vertices.size(); }
156 virtual int add_vertex() {
vertices.push_back(vertex_type());
return (
int)get_nr_vertices() - 1; }
160 const vertex_type& vertex(
int vi)
const {
return vertices[vi]; }
162 vertex_type& vertex(
int vi) {
return vertices[vi]; }
188 int last_vi = face(fi, si).back();
189 for (
int vi : face(fi, si)) {
190 center += position(vi);
191 normal += cross(position(last_vi), position(vi));
195 center *= T(1)/face(fi, si).
size();
196 return plane_type(normal(0), normal(1), normal(2), -dot(center, normal));
201 return convex_polyhedron_base::add_shell();
206 convex_polyhedron_base::del_shell(si);
211 convex_polyhedron_base::copy_shell(si_source, si_target);
216 convex_polyhedron_base::swap_shells(si0, si1);
219 int add_face(
int si = 0) {
221 return convex_polyhedron_base::add_face(si);
224 void del_face(
int fi,
int si = 0) {
226 convex_polyhedron_base::del_face(fi, si);
235 void compute_vertex_locations(
const plane_type& plane, std::vector<VertexPlaneLocation>& vertex_locations, T epsilon = 16 * std::numeric_limits<T>::epsilon(), std::vector<T>* vertex_signed_distances_ptr = 0)
const;
237 bool check_face_locations(
unsigned si,
const std::vector<VertexPlaneLocation>& vertex_locations, std::vector<FacePlaneLocation>& face_locations)
const;
239 bool ensure_vertex_location_consistency(
unsigned si,
const std::vector<T>& vertex_signed_distances, std::vector<VertexPlaneLocation>& vertex_locations,
const std::vector<FacePlaneLocation>& face_locations, T epsilon = 16 * std::numeric_limits<T>::epsilon(), T epsilon_flexibiliy = 8);
243 int clip_to_inside_of_plane(
unsigned si,
const plane_type& clip_plane,
bool keep_original_shell =
false, T epsilon = 16 * std::numeric_limits<T>::epsilon(), T epsilon_flexibiliy = 8);
247 int clip_to_outside_of_plane(
unsigned si,
const plane_type& clip_plane,
bool keep_original_shell =
false, T epsilon = 16 * std::numeric_limits<T>::epsilon(), T epsilon_flexibiliy = 8);
251 std::pair<int,int>
split_at_plane(
unsigned si,
const plane_type& split_plane,
bool keep_original_shell =
false, T epsilon = 16 * std::numeric_limits<T>::epsilon(), T epsilon_flexibiliy = 8, std::vector<VertexPlaneLocation>* vertex_locations_ptr = 0);
257 template <
typename T, cgv::type::
int32_type TCDim>
263 for (i = 0; i < 8; ++i) {
265 for (c = 0; c < 3; ++c) {
267 if (c < texcoord_dim)
268 V.texcoord(c) = ((i & (1 << c)) == 0) ? T(0) : T(1);
272 int si = add_shell();
273 shell_type& S = shell(si);
276 for (i = 0; i < 6; ++i) {
277 int fi = add_face(si);
280 P(i / 2) = ((i & 1) == 0) ? T(1) : T(-1);
282 face_type& F = face(fi, si);
283 for (
unsigned j = 0; j < 4; ++j)
284 F.push_back(box_get_face_vertex_index(i, j));
289 template <
typename T, cgv::type::
int32_type TCDim>
293 while (get_nr_shells() > 0)
294 del_shell((
int)get_nr_shells() - 1);
298 template <
typename T, cgv::type::
int32_type TCDim>
302 const shell_type& S = shell(si);
303 unsigned nr_faces = (unsigned)get_nr_faces(si);
304 unsigned nr_face_touches = 0;
305 face_locations.resize(nr_faces);
306 for (
unsigned fi = 0; fi < nr_faces; ++fi) {
307 const face_type& F = S[fi];
308 face_locations[fi] = get_face_plane_location(face(fi, si), vertex_locations);
309 if (face_locations[fi] == FPL_TOUCH_FACE)
311 if (!is_valid_face_location(face_locations[fi]))
314 if (nr_face_touches > 1)
320 template <
typename T, cgv::type::
int32_type TCDim>
327 template <
typename T, cgv::type::
int32_type TCDim>
330 std::pair<int, int> res = split_at_plane(si, clip_plane, keep_original_shell, epsilon, epsilon_flexibiliy);
331 if (res.second == -1)
335 if (res.first < res.second) {
336 del_shell(res.second);
339 copy_shell(res.first, res.second);
340 del_shell(res.first);
345 template <
typename T, cgv::type::
int32_type TCDim>
348 std::pair<int, int> res = split_at_plane(si, clip_plane, keep_original_shell, epsilon, epsilon_flexibiliy);
351 if (res.second == -1)
353 if (res.second < res.first) {
354 del_shell(res.first);
357 copy_shell(res.second, res.first);
358 del_shell(res.second);
363 template <
typename T, cgv::type::
int32_type TCDim>
368 vertex_locations.resize(get_nr_vertices());
369 std::vector<T> vertex_signed_distances_local;
370 std::vector<T>& vertex_signed_distances = vertex_signed_distances_ptr ? *vertex_signed_distances_ptr : vertex_signed_distances_local;
371 vertex_signed_distances.resize(get_nr_vertices());
372 for (vi = 0; vi < get_nr_vertices(); ++vi) {
374 vertex_signed_distances[vi] = sd;
375 vertex_locations[vi] = (sd < -epsilon) ? VPL_INSIDE : ((sd > epsilon) ? VPL_OUTSIDE : VPL_TOUCH);
380 template <
typename T, cgv::type::
int32_type TCDim>
384 std::vector<VertexPlaneLocation> vertex_locations_local;
385 std::vector<VertexPlaneLocation>& vertex_locations = vertex_locations_ptr ? *vertex_locations_ptr : vertex_locations_local;
386 std::vector<T> vertex_signed_distances;
387 compute_vertex_locations(split_plane, vertex_locations, epsilon, &vertex_signed_distances);
390 std::vector<FacePlaneLocation> face_locations;
391 if (!check_face_locations(si, vertex_locations, face_locations)) {
392 if (!ensure_vertex_location_consistency(si, vertex_signed_distances, vertex_locations, face_locations)) {
393 std::cerr <<
"found case that could not be made consistent" << std::endl;
399 VertexPlaneLocation shell_location = shell_location_side(face_locations);
401 if (shell_location == VPL_INSIDE) {
403 if (!keep_original_shell)
404 return std::pair<int, int>(si, -1);
406 int new_si = add_shell();
407 copy_shell(si, new_si);
408 return std::pair<int, int>(new_si, -1);
411 if (shell_location == VPL_OUTSIDE) {
413 if (!keep_original_shell)
414 return std::pair<int, int>(-1, si);
416 int new_si = add_shell();
417 copy_shell(si, new_si);
418 return std::pair<int, int>(-1, new_si);
422 std::vector<std::pair<int, int> > new_face_halfedges;
424 std::map<std::pair<int, int>,
int> edge_vertex_indices;
426 std::map<std::pair<int, int>, FacePlaneLocation> edge_touch_events;
429 int si_inside = add_shell();
430 int si_outside = add_shell();
432 for (
unsigned fi = 0; fi < get_nr_faces(si); ++fi) {
433 switch (face_locations[fi]) {
435 case FPL_OUTSIDE + FPL_TOUCH_EDGE:
438 std::pair<int, int> halfedge = extract_touching_halfedge(face(fi,si), vertex_locations);
439 std::pair<int, int> edge(std::min(halfedge.first, halfedge.second), std::max(halfedge.first, halfedge.second));
440 auto ete_iter = edge_touch_events.find(edge);
442 if (ete_iter != edge_touch_events.end()) {
444 if (ete_iter->second == FPL_INSIDE + FPL_TOUCH_EDGE)
445 new_face_halfedges.push_back(std::pair<int, int>(halfedge.second, halfedge.first));
447 edge_touch_events.erase(ete_iter);
451 edge_touch_events[edge] = FacePlaneLocation(FPL_OUTSIDE + FPL_TOUCH_EDGE);
453 case FPL_OUTSIDE + FPL_TOUCH_NOTHING:
454 case FPL_OUTSIDE + FPL_TOUCH_VERTEX:
456 int fi_outside = add_face(si_outside);
457 face(fi_outside, si_outside) = face(fi, si);
458 face_plane(fi_outside, si_outside) = face_plane(fi, si);
462 case FPL_INSIDE + FPL_TOUCH_EDGE:
465 std::pair<int, int> halfedge = extract_touching_halfedge(face(fi, si), vertex_locations);
466 std::pair<int, int> edge(std::min(halfedge.first, halfedge.second), std::max(halfedge.first, halfedge.second));
467 auto ete_iter = edge_touch_events.find(edge);
469 if (ete_iter != edge_touch_events.end()) {
471 if (ete_iter->second == FPL_OUTSIDE + FPL_TOUCH_EDGE)
472 new_face_halfedges.push_back(halfedge);
474 edge_touch_events.erase(ete_iter);
478 edge_touch_events[edge] = FacePlaneLocation(FPL_INSIDE + FPL_TOUCH_EDGE);
480 case FPL_INSIDE + FPL_TOUCH_NOTHING:
481 case FPL_INSIDE + FPL_TOUCH_VERTEX:
483 int fi_inside = add_face(si_inside);
484 face(fi_inside, si_inside) = face(fi, si);
485 face_plane(fi_inside, si_inside) = face_plane(fi, si);
491 int fi_outside = add_face(si_outside);
492 int fi_inside = add_face(si_inside);
493 face(fi_outside, si_outside) = face(fi_inside, si_inside) = face(fi, si);
494 face_plane(fi_outside, si_outside) = -face_plane(fi, si);
495 face_plane(fi_inside, si_inside) = face_plane(fi, si);
496 if (orientation_match(split_plane, face_plane(fi, si)))
497 change_orientation(face(fi_outside, si_outside));
499 change_orientation(face(fi_inside, si_inside));
503 case FPL_SPLIT_TWO_VERTICES:
504 case FPL_SPLIT_VERTEX_EDGE:
505 case FPL_SPLIT_TWO_EDGES:
507 const face_type& F = face(fi, si);
509 int fi_outside = add_face(si_outside);
510 int fi_inside = add_face(si_inside);
511 face_type& F_outside = face(fi_outside, si_outside);
512 face_type& F_inside = face(fi_inside, si_inside);
514 face_plane(fi_outside, si_outside) = face_plane(fi_inside, si_inside) = face_plane(fi, si);
516 std::vector<int> split_vis;
517 int last_vi = F.back();
518 VertexPlaneLocation last_vertex_location = vertex_locations[last_vi];
519 for (
unsigned i = 0; i < F.size(); ++i) {
521 VertexPlaneLocation vertex_location = vertex_locations[vi];
523 if (vertex_location == VPL_TOUCH) {
524 F_outside.push_back(vi);
525 F_inside.push_back(vi);
526 split_vis.push_back(vi);
527 if (split_vis.size() == 2 && last_vertex_location == VPL_INSIDE)
528 std::swap(split_vis[0], split_vis[1]);
532 if (last_vertex_location != VPL_TOUCH && vertex_location != last_vertex_location) {
534 std::pair<int, int> edge(std::min(last_vi, vi), std::max(last_vi, vi));
535 auto evi_iter = edge_vertex_indices.find(edge);
538 if (evi_iter != edge_vertex_indices.end()) {
539 split_vi = evi_iter->second;
540 edge_vertex_indices.erase(evi_iter);
544 split_vi = (int)vertices.size();
545 T distance = abs(vertex_signed_distances[vi]);
546 T last_distance = abs(vertex_signed_distances[last_vi]);
547 T lambda = last_distance / (last_distance + distance);
549 split_vertex.position = (T(1) - lambda) * position(last_vi) + lambda * position(vi);
550 split_vertex.texcoord = (T(1) - lambda) * texcoord(last_vi) + lambda * texcoord(vi);
551 vertices.push_back(split_vertex);
552 vertex_locations.push_back(VPL_TOUCH);
553 edge_vertex_indices[edge] = split_vi;
555 split_vis.push_back(split_vi);
556 if (split_vis.size() == 2 && last_vertex_location == VPL_INSIDE)
557 std::swap(split_vis[0], split_vis[1]);
560 F_outside.push_back(split_vi);
561 F_inside.push_back(split_vi);
564 if (vertex_location == VPL_INSIDE)
565 F_inside.push_back(vi);
567 F_outside.push_back(vi);
570 last_vertex_location = vertex_location;
572 if (split_vis.size() != 2) {
573 std::cerr <<
"expected two split vertices but found " << split_vis.size() << std::endl;
576 new_face_halfedges.push_back(std::pair<int, int>(split_vis[0], split_vis[1]));
580 std::cerr <<
"did not expect face plane location " << face_locations[fi] << std::endl;
587 int fi_outside = add_face(si_outside);
588 int fi_inside = add_face(si_inside);
589 face_type& F_outside = face(fi_outside, si_outside);
590 face_type& F_inside = face(fi_inside, si_inside);
591 face_plane(fi_outside, si_outside) = -split_plane;
592 face_plane(fi_inside, si_inside) = split_plane;
595 F_outside.resize(new_face_halfedges.size());
596 F_outside[0] = new_face_halfedges[0].first;
597 int last_vi = F_outside[1] = new_face_halfedges[0].second;
599 for (
unsigned i = 2; i < F_outside.size(); ++i) {
601 for (
unsigned k = k0; k < new_face_halfedges.size(); ++k) {
602 if (new_face_halfedges[k].first == last_vi) {
603 last_vi = F_outside[i] = new_face_halfedges[k].second;
605 std::swap(new_face_halfedges[k0], new_face_halfedges[k]);
612 std::cerr <<
"loop of new face not complete" << std::endl;
616 if (k0 < new_face_halfedges.size()-1) {
617 std::cerr <<
"loop of new face does not use all new halfedges" << std::endl;
620 if (new_face_halfedges.back().first != F_outside.back() ||
621 new_face_halfedges.back().second != F_outside.front()) {
622 std::cerr <<
"loop of new face does is not closed by last new halfedge" << std::endl;
626 std::copy(F_outside.rbegin(), F_outside.rend(), std::back_inserter(F_inside));
629 if (keep_original_shell)
630 return std::pair<int, int>(si_inside, si_outside);
632 copy_shell(si_outside, si);
633 del_shell(si_outside);
634 return std::pair<int, int>(si_inside, si);
638 template <
typename T, cgv::type::
int32_type TCDim>
642 std::vector<VertexPlaneLocation> vertex_locations;
643 std::vector<T> vertex_signed_distances;
644 compute_vertex_locations(
plane, vertex_locations, epsilon, &vertex_signed_distances);
647 std::vector<FacePlaneLocation> face_locations;
648 if (!check_face_locations(si, vertex_locations, face_locations)) {
649 std::cerr <<
"no valid face locations found" << std::endl;
650 return std::vector<vertex_type>();
654 if (shell_location_side(face_locations) != VPL_TOUCH)
655 return std::vector<vertex_type>();
658 for (
unsigned fi = 0; fi < face_locations.size(); ++fi) {
659 if (face_locations[fi] == FPL_TOUCH_FACE) {
660 std::vector<vertex_type> intersection;
661 const face_type& F = face(fi, si);
662 if (orientation_match(face_plane(fi, si),
plane)) {
663 for (
auto v = F.begin(); v != F.end(); ++v)
664 intersection.push_back(vertices[*v]);
667 for (
auto v = F.rbegin(); v != F.rend(); ++v)
668 intersection.push_back(vertices[*v]);
674 std::vector<std::pair<int, int> > new_face_halfedges;
676 std::map<std::pair<int, int>,
int> edge_vertex_indices;
678 std::map<std::pair<int, int>, FacePlaneLocation> edge_touch_events;
680 std::vector<vertex_type> split_vertices;
683 for (
unsigned fi = 0; fi < get_nr_faces(si); ++fi) {
684 switch (face_locations[fi]) {
686 case FPL_OUTSIDE + FPL_TOUCH_EDGE:
687 case FPL_INSIDE + FPL_TOUCH_EDGE:
690 std::pair<int, int> halfedge = extract_touching_halfedge(face(fi, si), vertex_locations);
691 std::pair<int, int> edge(std::min(halfedge.first, halfedge.second), std::max(halfedge.first, halfedge.second));
692 auto ete_iter = edge_touch_events.find(edge);
694 if (ete_iter != edge_touch_events.end()) {
696 if (ete_iter->second != face_locations[fi]) {
697 if (ete_iter->second == FPL_OUTSIDE + FPL_TOUCH_EDGE)
698 std::swap(halfedge.first, halfedge.second);
699 new_face_halfedges.push_back(halfedge);
702 edge_touch_events.erase(ete_iter);
706 edge_touch_events[edge] = face_locations[fi];
710 case FPL_SPLIT_TWO_VERTICES:
711 case FPL_SPLIT_VERTEX_EDGE:
712 case FPL_SPLIT_TWO_EDGES:
714 std::vector<int> split_vis;
715 const face_type& F = face(fi, si);
716 int last_vi = F.back();
717 VertexPlaneLocation last_vertex_location = vertex_locations[last_vi];
719 VertexPlaneLocation vertex_location = vertex_locations[vi];
721 if (vertex_location == VPL_TOUCH) {
722 split_vis.push_back(vi);
723 if (split_vis.size() == 2 && last_vertex_location == VPL_OUTSIDE)
724 std::swap(split_vis[0], split_vis[1]);
728 if (last_vertex_location != VPL_TOUCH && vertex_location != last_vertex_location) {
730 std::pair<int, int> edge(std::min(last_vi, vi), std::max(last_vi, vi));
731 auto evi_iter = edge_vertex_indices.find(edge);
734 if (evi_iter != edge_vertex_indices.end()) {
735 split_vi = evi_iter->second;
736 edge_vertex_indices.erase(evi_iter);
740 split_vi = -1-(int)split_vertices.size();
741 T distance = abs(vertex_signed_distances[vi]);
742 T last_distance = abs(vertex_signed_distances[last_vi]);
743 T lambda = last_distance / (last_distance + distance);
745 split_vertex.position = (T(1) - lambda) * position(last_vi) + lambda * position(vi);
746 split_vertex.texcoord = (T(1) - lambda) * texcoord(last_vi) + lambda * texcoord(vi);
747 split_vertices.push_back(split_vertex);
748 edge_vertex_indices[edge] = split_vi;
750 split_vis.push_back(split_vi);
751 if (split_vis.size() == 2 && last_vertex_location == VPL_OUTSIDE)
752 std::swap(split_vis[0], split_vis[1]);
756 last_vertex_location = vertex_location;
758 if (split_vis.size() != 2) {
759 std::cerr <<
"expected two split vertices but found " << split_vis.size() << std::endl;
762 new_face_halfedges.push_back(std::pair<int, int>(split_vis[0], split_vis[1]));
765 case FPL_INSIDE + FPL_TOUCH_NOTHING:
766 case FPL_INSIDE + FPL_TOUCH_VERTEX:
767 case FPL_OUTSIDE + FPL_TOUCH_NOTHING:
768 case FPL_OUTSIDE + FPL_TOUCH_VERTEX:
771 std::cerr <<
"did not expect face plane location " << face_locations[fi] << std::endl;
776 std::vector<vertex_type> intersection;
779 intersection.resize(new_face_halfedges.size());
780 int fst_vi = new_face_halfedges[0].first;
781 int last_vi = new_face_halfedges[0].second;
782 intersection[0] = fst_vi < 0 ? split_vertices[-fst_vi-1] : vertices[fst_vi];
783 intersection[1] = last_vi < 0 ? split_vertices[-last_vi-1] : vertices[last_vi];
785 for (
unsigned i = 2; i < intersection.size(); ++i) {
787 for (
unsigned k = k0; k < new_face_halfedges.size(); ++k) {
788 if (new_face_halfedges[k].first == last_vi) {
789 last_vi = new_face_halfedges[k].second;
790 intersection[i] = last_vi < 0 ? split_vertices[-last_vi-1] : vertices[last_vi];
792 std::swap(new_face_halfedges[k0], new_face_halfedges[k]);
799 std::cerr <<
"loop of new face not complete" << std::endl;
803 if (k0 < new_face_halfedges.size() - 1) {
804 std::cerr <<
"loop of new face does not use all new halfedges" << std::endl;
807 if (new_face_halfedges.back().first != last_vi ||
808 new_face_halfedges.back().second != fst_vi) {
809 std::cerr <<
"loop of new face does is not closed by last new halfedge" << std::endl;
818 #include <cgv/config/lib_end.h>