cgv
Loading...
Searching...
No Matches
convex_polyhedron.h
1#pragma once
2
3#include <map>
4#include <iterator>
5#include <cgv/math/fvec.h>
6#include <cgv/media/axis_aligned_box.h>
7#include <cgv/media/color.h>
8
9#include "../lib_begin.h"
10
11namespace cgv {
12 namespace media {
13 namespace mesh {
14
16{
17public:
20 VPL_INSIDE = -1,
21 VPL_TOUCH = 0,
22 VPL_OUTSIDE = 1
23 };
26 // valid cases
27 FPL_OUTSIDE = 0, // face is outside (is refined by TOUCH-versions by adding or or-ring together)
28 FPL_INSIDE = 3, // face is inside (is refined by TOUCH-versions by adding or or-ring together)
29
30 FPL_TOUCH_NOTHING = 0, // face is not touching and is not split but completely outside or inside (v#+|-=0, v#0=0)[e#0=0, e#s=0, #t=0]
31 FPL_TOUCH_VERTEX = 1, // one vertex of face touches plane (v#+|-=0, v#0=1)[e#0=0, e#s=0, #t=0]
32 FPL_TOUCH_EDGE = 2, // one edge of face touches plane (v#+|-=0, v#0=2, e#0=1)[e#s=0, #t=0]
33
34 FPL_TOUCH_FACE = 6, // whole face touches plane (v#+|-=0, v#0=n)[e#0=0, e#s=0, #t=0]
35
36 FPL_SPLIT_TWO_VERTICES, // face is split through two not adjacent vertices (#t=2, v#0=2, e#s=0)[e#0=0, v#+>0, v#->0]
37 FPL_SPLIT_VERTEX_EDGE, // face is split through one vertex and one edge (#t=2, v#0=1, e#s=1)[e#0=0, v#+>0, v#->0]
38 FPL_SPLIT_TWO_EDGES, // face is split through two edges (#t=2, v#0=0, e#s=2)[e#0=0, v#+>0, v#->0]
39
40 // causes of invalidity
41 FPL_TWO_NON_ADJACENT_VERTICES_TOUCH,// two non adjacent vertices touch plane (v#+|-=0, v#0=2, e#0=0)
42 FPL_INCOMLETE_TOUCH_FACE, // more than two and less than all vertices touch plane (v#0>2, v#0<n)
43 FPL_TOUCH_AND_SPLIT, // in addition to a split we have further touch events (#t=2, v#0+e#s>2)
44 FPL_MULTI_SPLIT, // more than one pair of inside->outside, outside->inside transitions found (#t>2)
45 FPL_ODD_TRANSITION_NUMBER, // the computed number of transitions is odd (#t%2=1)
46
47 FPL_UNCONSIDERED_CASE,
48
49 FPL_FIRST_INVALID_CASE = FPL_TWO_NON_ADJACENT_VERTICES_TOUCH
50 };
51
53 typedef std::vector<int> face_type;
54
56 typedef std::vector<face_type> shell_type;
57
58protected:
60 std::vector<shell_type> shells;
61public:
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);
78
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;
86 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]; }
110
113 virtual size_t get_nr_vertices() const = 0;
115 virtual void del_vertex(int vi) = 0;
117 void remove_redundant_vertices();
119};
120
122
123template <typename T, cgv::type::int32_type TCDim = 2>
125{
126public:
128 typedef T coord_type;
141 {
142 point_type position;
143 texcoord_type texcoord;
144 };
145protected:
147 std::vector<vertex_type> vertices;
149 std::vector<std::vector<plane_type> > face_planes;
150public:
154 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; }
158 void del_vertex(int vi) { vertices.erase(vertices.begin() + vi); }
160 const vertex_type& vertex(int vi) const { return vertices[vi]; }
162 vertex_type& vertex(int vi) { return vertices[vi]; }
164 const point_type& position(int vi) const { return vertices[vi].position; }
166 point_type& position(int vi) { return vertices[vi].position; }
168 const texcoord_type& texcoord(int vi) const { return vertices[vi].texcoord; }
170 texcoord_type& texcoord(int vi) { return vertices[vi].texcoord; }
172
173
177 static bool orientation_match(const plane_type& p, const plane_type& q) {
178 return dot(reinterpret_cast<const point_type&>(p), reinterpret_cast<const point_type&>(q)) > 0;
179 }
181 const plane_type& face_plane(int fi, int si = 0) const { return face_planes[si][fi]; }
183 plane_type& face_plane(int fi, int si = 0) { return face_planes[si][fi]; }
185 plane_type compute_face_plane(int fi, int si = 0) const {
186 point_type center(0, 0, 0);
187 point_type normal(0, 0, 0);
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));
192 last_vi = vi;
193 }
194 normal.normalize();
195 center *= T(1)/face(fi, si).size();
196 return plane_type(normal(0), normal(1), normal(2), -dot(center, normal));
197 }
199 int add_shell() {
200 face_planes.push_back(std::vector<plane_type>());
202 }
204 void del_shell(int si) {
205 face_planes.erase(face_planes.begin() + si);
207 }
209 void copy_shell(int si_source, int si_target) {
210 face_planes[si_target] = face_planes[si_source];
211 convex_polyhedron_base::copy_shell(si_source, si_target);
212 }
214 void swap_shells(int si0, int si1) {
215 std::swap(face_planes[si0], face_planes[si1]);
217 }
219 int add_face(int si = 0) {
220 face_planes[si].push_back(plane_type());
221 return convex_polyhedron_base::add_face(si);
222 }
224 void del_face(int fi, int si = 0) {
225 face_planes[si].erase(face_planes[si].begin() + fi);
226 convex_polyhedron_base::del_face(fi, si);
227 }
229
231 void construct_box(const box_type& box);
233 void clear();
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);
241
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);
245
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);
249
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);
253 std::vector<vertex_type> compute_intersection_polygon(unsigned si, const plane_type& plane, T epsilon = 16 * std::numeric_limits<T>::epsilon(), T epsilon_flexibiliy = 8) const;
254};
255
257template <typename T, cgv::type::int32_type TCDim>
259{
260 int i, c;
261 // construct corner vertices
262 vertices.resize(8);
263 for (i = 0; i < 8; ++i) {
264 vertex_type &V = vertices[i];
265 for (c = 0; c < 3; ++c) {
266 V.position(c) = ((i & (1 << c)) == 0) ? box.get_min_pnt()(c) : box.get_max_pnt()(c);
267 if (c < texcoord_dim)
268 V.texcoord(c) = ((i & (1 << c)) == 0) ? T(0) : T(1);
269 }
270 }
271 // construct node
272 int si = add_shell();
273 shell_type& S = shell(si);
274 // construct faces
275 S.clear();
276 for (i = 0; i < 6; ++i) {
277 int fi = add_face(si);
278 plane_type& P = face_plane(fi,si);
279 P.zeros();
280 P(i / 2) = ((i & 1) == 0) ? T(1) : T(-1);
281 P(3) = ((i & 1) == 0) ? -box.get_max_pnt()(i / 2) : box.get_min_pnt()(i / 2);
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));
285 }
286}
287
289template <typename T, cgv::type::int32_type TCDim>
291{
292 vertices.clear();
293 while (get_nr_shells() > 0)
294 del_shell((int)get_nr_shells() - 1);
295}
296
298template <typename T, cgv::type::int32_type TCDim>
299bool convex_polyhedron<T, TCDim>::check_face_locations(unsigned si, const std::vector<VertexPlaneLocation>& vertex_locations, std::vector<FacePlaneLocation>& face_locations) const
300{
301 bool valid = true;
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)
310 ++nr_face_touches;
311 if (!is_valid_face_location(face_locations[fi]))
312 valid = false;
313 }
314 if (nr_face_touches > 1)
315 valid = false;
316 return valid;
317}
318
320template <typename T, cgv::type::int32_type TCDim>
321bool convex_polyhedron<T, TCDim>::ensure_vertex_location_consistency(unsigned ni, const std::vector<T>& vertex_signed_distances, std::vector<VertexPlaneLocation>& vertex_locations, const std::vector<FacePlaneLocation>& face_locations, T epsilon, T epsilon_flexibiliy)
322{
323 return false;
324}
325
327template <typename T, cgv::type::int32_type TCDim>
328int convex_polyhedron<T, TCDim>::clip_to_inside_of_plane(unsigned si, const plane_type& clip_plane, bool keep_original_shell, T epsilon, T epsilon_flexibiliy)
329{
330 std::pair<int, int> res = split_at_plane(si, clip_plane, keep_original_shell, epsilon, epsilon_flexibiliy);
331 if (res.second == -1)
332 return res.first;
333 if (res.first == -1)
334 return -1;
335 if (res.first < res.second) {
336 del_shell(res.second);
337 return res.first;
338 }
339 copy_shell(res.first, res.second);
340 del_shell(res.first);
341 return res.second;
342}
343
345template <typename T, cgv::type::int32_type TCDim>
346int convex_polyhedron<T, TCDim>::clip_to_outside_of_plane(unsigned si, const plane_type& clip_plane, bool keep_original_shell, T epsilon, T epsilon_flexibiliy)
347{
348 std::pair<int, int> res = split_at_plane(si, clip_plane, keep_original_shell, epsilon, epsilon_flexibiliy);
349 if (res.first == -1)
350 return res.second;
351 if (res.second == -1)
352 return -1;
353 if (res.second < res.first) {
354 del_shell(res.first);
355 return res.second;
356 }
357 copy_shell(res.second, res.first);
358 del_shell(res.second);
359 return res.first;
360}
361
362
363template <typename T, cgv::type::int32_type TCDim>
364void convex_polyhedron<T, TCDim>::compute_vertex_locations(const plane_type& plane, std::vector<VertexPlaneLocation>& vertex_locations, T epsilon, std::vector<T>* vertex_signed_distances_ptr) const
365{
366 // compute signed distances of vertices from clipping plane and compute sign including 0
367 unsigned vi;
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) {
373 T sd = dot(position(vi), reinterpret_cast<const point_type&>(plane)) + plane(3);
374 vertex_signed_distances[vi] = sd;
375 vertex_locations[vi] = (sd < -epsilon) ? VPL_INSIDE : ((sd > epsilon) ? VPL_OUTSIDE : VPL_TOUCH);
376 }
377}
378
380template <typename T, cgv::type::int32_type TCDim>
381std::pair<int, int> convex_polyhedron<T, TCDim>::split_at_plane(unsigned si, const plane_type& split_plane, bool keep_original_shell, T epsilon, T epsilon_flexibiliy, std::vector<VertexPlaneLocation>* vertex_locations_ptr)
382{
383 // first compute vertex locations
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);
388
389 // next compute face locations and check for invalid cases
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;
394 abort();
395 }
396 }
397
398 // check if intersection is empty
399 VertexPlaneLocation shell_location = shell_location_side(face_locations);
400 // if complete shell is inside, no split computation is necessary
401 if (shell_location == VPL_INSIDE) {
402 // in case the original shell is not kept, reuse it as result for interior shell
403 if (!keep_original_shell)
404 return std::pair<int, int>(si, -1);
405 // otherwise construct copy of shell
406 int new_si = add_shell();
407 copy_shell(si, new_si);
408 return std::pair<int, int>(new_si, -1);
409 }
410 // if complete shell is outside
411 if (shell_location == VPL_OUTSIDE) {
412 // in case the original shell is not kept, reuse it as result for exterior shell
413 if (!keep_original_shell)
414 return std::pair<int, int>(-1, si);
415 // otherwise construct copy of shell
416 int new_si = add_shell();
417 copy_shell(si, new_si);
418 return std::pair<int, int>(-1, new_si);
419 }
420
421 // collect halfedges of face of new plane
422 std::vector<std::pair<int, int> > new_face_halfedges;
423 // use a map to hash the newly created vertices per edge
424 std::map<std::pair<int, int>, int> edge_vertex_indices;
425 // use a map to hash touched edges with their locations to allow adding edges of the new face
426 std::map<std::pair<int, int>, FacePlaneLocation> edge_touch_events;
427
428 // create two new shells
429 int si_inside = add_shell();
430 int si_outside = add_shell();
431 // go through all faces
432 for (unsigned fi = 0; fi < get_nr_faces(si); ++fi) {
433 switch (face_locations[fi]) {
434 // outside faces that do not touch are only touch in vertex or edge are kept
435 case FPL_OUTSIDE + FPL_TOUCH_EDGE:
436 {
437 // check touching edges for matching insides to also consider them for edges of the new face
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);
441 // if yes
442 if (ete_iter != edge_touch_events.end()) {
443 // check for opposite side
444 if (ete_iter->second == FPL_INSIDE + FPL_TOUCH_EDGE)
445 new_face_halfedges.push_back(std::pair<int, int>(halfedge.second, halfedge.first));
446 // erase matched and unmatched events
447 edge_touch_events.erase(ete_iter);
448 }
449 // otherwise create new edge touch event
450 else
451 edge_touch_events[edge] = FacePlaneLocation(FPL_OUTSIDE + FPL_TOUCH_EDGE);
452 }
453 case FPL_OUTSIDE + FPL_TOUCH_NOTHING:
454 case FPL_OUTSIDE + FPL_TOUCH_VERTEX:
455 {
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);
459 break;
460 }
461 // inside faces that do not touch are only touch in vertex or edge are kept
462 case FPL_INSIDE + FPL_TOUCH_EDGE:
463 {
464 // check touching edges for matching outsides to also consider them for edges of the new face
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);
468 // if yes
469 if (ete_iter != edge_touch_events.end()) {
470 // check for opposite side
471 if (ete_iter->second == FPL_OUTSIDE + FPL_TOUCH_EDGE)
472 new_face_halfedges.push_back(halfedge);
473 // erase matched and unmatched events
474 edge_touch_events.erase(ete_iter);
475 }
476 // otherwise create new edge touch event
477 else
478 edge_touch_events[edge] = FacePlaneLocation(FPL_INSIDE + FPL_TOUCH_EDGE);
479 }
480 case FPL_INSIDE + FPL_TOUCH_NOTHING:
481 case FPL_INSIDE + FPL_TOUCH_VERTEX:
482 {
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);
486 break;
487 }
488 // completely touching faces are kept and dublicated
489 case FPL_TOUCH_FACE:
490 {
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));
498 else
499 change_orientation(face(fi_inside, si_inside));
500 break;
501 }
502 // in the split cases we need to iterate the halfedges
503 case FPL_SPLIT_TWO_VERTICES:
504 case FPL_SPLIT_VERTEX_EDGE:
505 case FPL_SPLIT_TWO_EDGES:
506 {
507 const face_type& F = face(fi, si);
508 // add an empty face on both sides
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);
513 // copy face plane to both sides
514 face_plane(fi_outside, si_outside) = face_plane(fi_inside, si_inside) = face_plane(fi, si);
515 // loop face vertex indices and update counts
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) {
520 int vi = F[i];
521 VertexPlaneLocation vertex_location = vertex_locations[vi];
522 // touching vertices go into both face loops
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]);
529 }
530 // if previous halfedge is split, generate split vertex and add to both loops
531 else {
532 if (last_vertex_location != VPL_TOUCH && vertex_location != last_vertex_location) {
533 // check if split vertex has already been created on inverse halfedge
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);
536 int split_vi;
537 // if yes, get vertex index and remove entry from map
538 if (evi_iter != edge_vertex_indices.end()) {
539 split_vi = evi_iter->second;
540 edge_vertex_indices.erase(evi_iter);
541 }
542 // otherwise create new vertex on edge by affine interpolation according to unsigned distances
543 else {
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);
548 vertex_type split_vertex;
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;
554 }
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]);
558
559 // add split vertex to both faces
560 F_outside.push_back(split_vi);
561 F_inside.push_back(split_vi);
562 }
563 // then add current vertex only to current loop
564 if (vertex_location == VPL_INSIDE)
565 F_inside.push_back(vi);
566 else
567 F_outside.push_back(vi);
568 }
569 last_vi = vi;
570 last_vertex_location = vertex_location;
571 }
572 if (split_vis.size() != 2) {
573 std::cerr << "expected two split vertices but found " << split_vis.size() << std::endl;
574 abort();
575 }
576 new_face_halfedges.push_back(std::pair<int, int>(split_vis[0], split_vis[1]));
577 break;
578 }
579 default:
580 std::cerr << "did not expect face plane location " << face_locations[fi] << std::endl;
581 abort();
582 }
583 }
584 // finally we need to add one face for the new plane on each side
585
586 // prepare new faces
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;
593
594 // first link the new face for the outside shell
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;
598 unsigned k0 = 1;
599 for (unsigned i = 2; i < F_outside.size(); ++i) {
600 bool found = false;
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;
604 if (k > k0)
605 std::swap(new_face_halfedges[k0], new_face_halfedges[k]);
606 ++k0;
607 found = true;
608 break;
609 }
610 }
611 if (!found) {
612 std::cerr << "loop of new face not complete" << std::endl;
613 abort();
614 }
615 }
616 if (k0 < new_face_halfedges.size()-1) {
617 std::cerr << "loop of new face does not use all new halfedges" << std::endl;
618 abort();
619 }
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;
623 abort();
624 }
625 // finally copy to inside shell in reverse order
626 std::copy(F_outside.rbegin(), F_outside.rend(), std::back_inserter(F_inside));
627
628 // in case original shell is kept, return pair of shell indices
629 if (keep_original_shell)
630 return std::pair<int, int>(si_inside, si_outside);
631 // otherwise overwrite original shell with outside shell and remove outside shell again
632 copy_shell(si_outside, si);
633 del_shell(si_outside);
634 return std::pair<int, int>(si_inside, si);
635}
636
638template <typename T, cgv::type::int32_type TCDim>
639std::vector<typename convex_polyhedron<T, TCDim>::vertex_type> convex_polyhedron<T, TCDim>::compute_intersection_polygon(unsigned si, const plane_type& plane, T epsilon, T epsilon_flexibiliy) const
640{
641 // first compute vertex locations
642 std::vector<VertexPlaneLocation> vertex_locations;
643 std::vector<T> vertex_signed_distances;
644 compute_vertex_locations(plane, vertex_locations, epsilon, &vertex_signed_distances);
645
646 // next compute face locations and check for invalid cases
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>();
651 }
652
653 // check if intersection is empty
654 if (shell_location_side(face_locations) != VPL_TOUCH)
655 return std::vector<vertex_type>();
656
657 // check if intersection corresponds to a touching face
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]);
665 }
666 else {
667 for (auto v = F.rbegin(); v != F.rend(); ++v)
668 intersection.push_back(vertices[*v]);
669 }
670 return intersection;
671 }
672 }
673 // collect halfedges of face of new plane
674 std::vector<std::pair<int, int> > new_face_halfedges;
675 // use a map to hash the newly created vertices per edge
676 std::map<std::pair<int, int>, int> edge_vertex_indices;
677 // use a map to hash touched edges with their locations to allow adding edges of the new face
678 std::map<std::pair<int, int>, FacePlaneLocation> edge_touch_events;
679 // keep a vector of all vertices created on split edges
680 std::vector<vertex_type> split_vertices;
681
682 // go through all faces
683 for (unsigned fi = 0; fi < get_nr_faces(si); ++fi) {
684 switch (face_locations[fi]) {
685 // outside faces can only contribute touching edges
686 case FPL_OUTSIDE + FPL_TOUCH_EDGE:
687 case FPL_INSIDE + FPL_TOUCH_EDGE:
688 {
689 // check touching edges for matching insides to also consider them for edges of the new face
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);
693 // if yes
694 if (ete_iter != edge_touch_events.end()) {
695 // check for opposite side
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);
700 }
701 // erase matched and unmatched events
702 edge_touch_events.erase(ete_iter);
703 }
704 // otherwise create new edge touch event
705 else
706 edge_touch_events[edge] = face_locations[fi];
707 break;
708 }
709 // in the split cases we need to iterate the halfedges
710 case FPL_SPLIT_TWO_VERTICES:
711 case FPL_SPLIT_VERTEX_EDGE:
712 case FPL_SPLIT_TWO_EDGES:
713 {
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];
718 for (int vi : F) {
719 VertexPlaneLocation vertex_location = vertex_locations[vi];
720 // touching vertices split the face
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]);
725 }
726 // if previous halfedge is split, generate split vertex and add to both loops
727 else {
728 if (last_vertex_location != VPL_TOUCH && vertex_location != last_vertex_location) {
729 // check if split vertex has already been created on inverse halfedge
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);
732 int split_vi;
733 // if yes, get vertex index and remove entry from map
734 if (evi_iter != edge_vertex_indices.end()) {
735 split_vi = evi_iter->second;
736 edge_vertex_indices.erase(evi_iter);
737 }
738 // otherwise create new vertex on edge by affine interpolation according to unsigned distances
739 else {
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);
744 vertex_type split_vertex;
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;
749 }
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]);
753 }
754 }
755 last_vi = vi;
756 last_vertex_location = vertex_location;
757 }
758 if (split_vis.size() != 2) {
759 std::cerr << "expected two split vertices but found " << split_vis.size() << std::endl;
760 abort();
761 }
762 new_face_halfedges.push_back(std::pair<int, int>(split_vis[0], split_vis[1]));
763 break;
764 }
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:
769 break;
770 default:
771 std::cerr << "did not expect face plane location " << face_locations[fi] << std::endl;
772 abort();
773 }
774 }
775 // finally we can construct intersection polygon
776 std::vector<vertex_type> intersection;
777
778 // first link the new face for the outside shell
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];
784 unsigned k0 = 1;
785 for (unsigned i = 2; i < intersection.size(); ++i) {
786 bool found = false;
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];
791 if (k > k0)
792 std::swap(new_face_halfedges[k0], new_face_halfedges[k]);
793 ++k0;
794 found = true;
795 break;
796 }
797 }
798 if (!found) {
799 std::cerr << "loop of new face not complete" << std::endl;
800 abort();
801 }
802 }
803 if (k0 < new_face_halfedges.size() - 1) {
804 std::cerr << "loop of new face does not use all new halfedges" << std::endl;
805 abort();
806 }
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;
810 abort();
811 }
812 return intersection;
813}
814 }
815 }
816}
817
818#include <cgv/config/lib_end.h>
A vector with zero based index.
Definition fvec.h:26
T normalize()
normalize the vector using the L2-Norm and return the length
Definition fvec.h:302
void zeros()
fill the vector with zeros
Definition fvec.h:133
An axis aligned box, defined by to points: min and max.
const fpnt_type & get_max_pnt() const
return a const reference to corner 7
const fpnt_type & get_min_pnt() const
return a const reference to corner 0
static bool is_valid_face_location(FacePlaneLocation face_location)
check if a face location corresponds to a valid configuration assuming planar convex faces
std::vector< shell_type > shells
vector of shells
VertexPlaneLocation
enumeration of different vertex locations with respect to plane
virtual void copy_shell(int si_source, int si_target)
copy a shell onto another one
virtual void del_shell(int si)
remove the shell of the given index, be careful as shell indices large si get implizitely decreased
virtual void del_vertex(int vi)=0
virtual method to be implemented in derived class
virtual int add_shell()
add a new shell and return shell index
static bool is_face_location_on_one_side(FacePlaneLocation face_location)
check if a face location is on one side of the plane only with potential touch events
static int to_index(VertexPlaneLocation vertex_location)
convert VertexPlaneLocation to an index in the range {0 .. 2}
size_t get_nr_shells() const
return the current number of shells
std::vector< int > face_type
a face is an oriented list of vertex indices
virtual size_t get_nr_vertices() const =0
return current number of vertices
FacePlaneLocation
enumeration of 11 valid and 5 invalid cases that a face can be located with respect to a plane after ...
static bool is_face_location_split(FacePlaneLocation face_location)
check if a face location corresponds to a split into parts (two for convex faces)
std::vector< face_type > shell_type
a shell is simply a list of faces
virtual void swap_shells(int si0, int si1)
swap two shells
simple mesh templated with per vertex position and texture coordinates and per face plane.
void construct_box(const box_type &box)
construct a polyhedron representing an axis aligned box
cgv::math::fvec< T, 3 > point_type
position type
void swap_shells(int si0, int si1)
swap two shells
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)
if the previous check is invalid, call this function to change the vertex signs in order to validate ...
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
construct a vector of vertex locations relative to given plane
plane_type & face_plane(int fi, int si=0)
return reference to plane of face fi in shell si
plane_type compute_face_plane(int fi, int si=0) const
compute a face plane from the vertex positions
cgv::math::fvec< T, TCDim > texcoord_type
texture coordinate type
std::vector< std::vector< plane_type > > face_planes
per shell a vector to store one plane per face
void copy_shell(int si_source, int si_target)
copy a shell onto another one
bool check_face_locations(unsigned si, const std::vector< VertexPlaneLocation > &vertex_locations, std::vector< FacePlaneLocation > &face_locations) const
compute face plane locations of the faces of one shell, store them in the vector face_locations and r...
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)
split the given shell into interior and exterior
size_t get_nr_vertices() const
return current number of vertices
std::vector< vertex_type > compute_intersection_polygon(unsigned si, const plane_type &plane, T epsilon=16 *std::numeric_limits< T >::epsilon(), T epsilon_flexibiliy=8) const
compute the intersection polygon between a shell and a plane
int add_shell()
add a new shell and return shell index
void del_vertex(int vi)
remove a vertex
const cgv::type::int32_type texcoord_dim
declare dimension of texture coordinates
const plane_type & face_plane(int fi, int si=0) const
return const reference to plane of face fi in shell si
cgv::math::fvec< T, 4 > plane_type
plane type
static bool orientation_match(const plane_type &p, const plane_type &q)
check if planes are oriented in the same normal direction
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)
compute the exterior of the given shell si with respect to the given clipping plane and either replac...
void del_shell(int si)
remove the shell of the given index, be careful as shell indices large si get implizitely decreased
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)
compute the interior of the given shell si with respect to the given clipping plane and either replac...
cgv::media::axis_aligned_box< T, 3 > box_type
type of axis aligned box
std::vector< vertex_type > vertices
vector to store vertices of all components in one vector
A plane of arbitrary dimension stored as a homogeneous vector that defines the coefficients of the pl...
Definition plane.h:14
int int32_type
this type provides an 32 bit signed integer type
the cgv namespace
Definition print.h:11