/* * This file is part of libpmregion * * File: PMRegion\_coverduration.cpp * Author: Florian Heinz 1 Coverduration Calculates the coverduration for all points covered by a PMRegion and compiles a new PMRegion from that. */ #include "PMRegion_internal.h" #ifndef PMREGION_DISABLE_COVERDURATION #include #include #include using namespace pmr; using namespace std; namespace pmr { void PMRegion::zthicknessprepare () { // Create a copy of the polyhedron and triangulate the faces zthicknesstmp = polyhedron; CGAL::Polygon_mesh_processing::triangulate_faces(zthicknesstmp); // Build an AABB tree for fast intersection tests zthicknesstree = new Tree(faces(zthicknesstmp).first, faces(zthicknesstmp).second, zthicknesstmp); zthicknesstree->accelerate_distance_queries(); // Create a tester to check, if a point is within the polyhedron boundary inside_tester = new Point_inside(*zthicknesstree); } /* zthickness calculates the thickness of the polyhedron wrt the * z axis at * the given xy coordinates. */ Kernel::FT PMRegion::zthickness (Point2d p2d) { if (ztcache.count(p2d) > 0) { return ztcache[p2d]; } Kernel::FT x = p2d.x(); Kernel::FT y = p2d.y(); // Create a line orthogonal to the z plane through point x/y Point3d p1(x, y, 0), p2(x, y, 1); Kernel::Line_3 line(p1, p2); // Calculate intersections between the line and the polyhedron boundary list sis; zthicknesstree->all_intersections(line, back_inserter(sis)); // Add all intersection points to an ordered set set zs; for (list::iterator it = sis.begin(); it != sis.end(); ++it) { if (Point3d *p = boost::get(&((*it)->first))) { zs.insert(p->z()); } } Kernel::FT zthickness = 0; Kernel::FT prev; bool prevvalid = false; // Iterate over all intersection points in z order for (set::iterator it = zs.begin(); it != zs.end(); ++it) { Kernel::FT cur = *it; if (prevvalid) { // For two consecutive points, calculate the middle point and test, // if it is inside the polyhedron. Point3d p3(x, y, (prev+cur)/2); if ((*inside_tester)(p3) == CGAL::ON_BOUNDED_SIDE) { // It is inside, so the length of the segment contributes // to the zthickness zthickness += (cur - prev); } } prev = cur; prevvalid = true; } ztcache[p2d] = zthickness; return zthickness; } /* The operation coverduration returns, for which time a given point is covered by a moving region. This function operates on a pre-calculated cdpolyhedron */ Kernel::FT PMRegion::coverduration (Kernel::FT x, Kernel::FT y) { // Create a line orthogonal to the z plane through point x/y Point3d p1(x, y, 0), p2(x, y, 1); Kernel::Line_3 line(p1, p2); // Create a copy of the polyhedron and triangulate the faces Polyhedron poly = polyhedron; Tree tree(faces(poly).first,faces(poly).second,poly); // Calculate intersections between the line and the polyhedron boundary list sis; tree.all_intersections(line, back_inserter(sis)); // Due to the specific characteristics of a cdpolyhedron, there can be // at most two intersections. One at (x,y,0) and the other at (x,y,z), // where z is the result value. for (list::iterator it = sis.begin(); it != sis.end(); ++it) { if (Point3d *p = boost::get(&((*it)->first))) { if (p->z() != 0) return p->z(); } } // The line was outside the polyhedron or intersected the polyhedron in // a single point, which has to be z=0 then. return 0; } /* projects the 3d facets of the polyhedron to 2d segments in the xy plane */ set getprojectedsegments(Polyhedron polyhedron) { set segs; // Iterate over all polyhedron facets for (Facet_iterator f = polyhedron.facets_begin(); f != polyhedron.facets_end(); f++) { Halfedge_facet_circulator h = f->facet_begin(), he(h); Point_2 prev; bool prevvalid = false; do { Point3d p3 = h->vertex()->point(); Point_2 p(p3.x(), p3.y()); if (prevvalid && prev != p) { // Create a 2d line segment for each polygon segment // Define an unambiguous order to prevent duplicates if (prev > p) { Segment_2 seg(p, prev); segs.insert(seg); } else { Segment_2 seg(prev, p); segs.insert(seg); } } prev = p; prevvalid = true; } while (++h != he); // Add last segment which closes the polygon Point3d p3 = he->vertex()->point(); Point_2 p(p3.x(), p3.y()); if (p != prev) { if (prev > p) { Segment_2 seg(p, prev); segs.insert(seg); } else { Segment_2 seg(prev, p); segs.insert(seg); } } } return segs; } /* Create non-intersecting subsegments from a set of intersecting 2d segments. * This creates for example 4 segments from 2 intersecting line segments */ list getsubsegments (set segs) { list subsegs; CGAL::compute_subcurves(segs.begin(), segs.end(), back_inserter(subsegs)); return subsegs; } /* Build an arrangement of non-intersecting polygons from a set of segments */ Arrangement getarrangementfromsegments (list subsegs) { Arrangement arr; for (list::iterator it = subsegs.begin(); it != subsegs.end(); it++) { CGAL::insert(arr, *it); } return arr; } /* Create 3d facets from an arrangement of 2d polygons. The z value of each vertex is the z thickness of the original polyhedron */ static pair, vector > > create3dfacets (Arrangement arr, PMRegion *p) { int _idx = 0; Arrangement::Face_const_iterator fit; Arrangement::Ccb_halfedge_const_circulator curr; map points; map indices; vector xpoints; vector > xpolygons; p->zthicknessprepare(); // Iterate over all 2d polygons in the interior of the arrangement for (fit = arr.faces_begin(); fit != arr.faces_end(); ++fit) { if (!fit->is_unbounded()) { curr = fit->outer_ccb(); Polygon poly; vector facet; // Iterate over all vertices of a polygon do { Point2d p2d = curr->target()->point(); poly.push_back(p2d); Point3d p3d; int idx; // Check, if the point was already processed if (points.count(p2d) == 0) { // Extend the 2d point to a 3d point with // the z-thickness of the polyhedron as // z coordinate Kernel::FT z = p->zthickness(p2d); p3d = Point3d(p2d.x(), p2d.y(), z); points[p2d] = p3d; indices[p3d] = idx = _idx++; // Insert it in the list of points xpoints.push_back(p3d); } else { // Point already exists, retrieve it and its index p3d = points[p2d]; idx = indices[p3d]; } // Add the index of the point to the current facet facet.push_back(idx); ++curr; } while (curr != fit->outer_ccb()); // Add the facet to the set of 3d polygons xpolygons.push_back(facet); } } // The outer boundary of the arrangement is added with all z // coordinates set to 0. This will be the baseplate of the // cdpolyhedron Arrangement::Face_iterator fi = arr.unbounded_face(); for (Arrangement::Hole_iterator fs = fi->holes_begin(); fs != fi->holes_end(); fs++) { Arrangement::Ccb_halfedge_circulator c = *fs, ec(c); vector facet; do { Point2d p2d = c->source()->point(); Point3d p3d(p2d.x(), p2d.y(), 0); facet.push_back(indices[p3d]); } while (++c != ec); xpolygons.push_back(facet); } return pair, vector > >(xpoints, xpolygons); } /* Create a polyhedron from a list of points and facets */ static Polyhedron createpolyhedronfromfacets (vector points, vector > polygons) { Polyhedron p; CGAL::Polygon_mesh_processing::orient_polygon_soup(points, polygons); CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, polygons, p); CGAL::Polygon_mesh_processing::triangulate_faces(p); return p; } /* Calculate a coverduration polyhedron from a polyhedron representing a * moving region. */ PMRegion PMRegion::createcdpoly() { // Project all edges of the polyhedron's boundary to 2d set segs = getprojectedsegments(polyhedron); // Create non-intersecting subsegments list subsegs = getsubsegments(segs); // Build a 2d arrangement from these segments Arrangement arr = getarrangementfromsegments(subsegs); // Create 3d facets from the 2d polygons in the arrangement pair, vector > > facets = create3dfacets(arr, this); // Build a polyhedron from these 3d facets Polyhedron p = createpolyhedronfromfacets(facets.first, facets.second); PMRegion ret(p); return ret; } /* Calculate a coverduration polyhedron from a polyhedron representing a * moving region related to a given baseregion. */ PMRegion PMRegion::createcdpoly(RList baseregion) { PMRegion cdpoly = createcdpoly(); Kernel::FT maxz = cdpoly.minmaxz().second; PMRegion baseregpoly = PMRegion::fromRegion(baseregion, -1, maxz+1, 0); return cdpoly * baseregpoly; } /* Change the base region for a coverduration polyhedron */ PMRegion PMRegion::restrictcdpoly(RList baseregion) { Kernel::FT maxz = minmaxz().second; PMRegion baseregpoly = PMRegion::fromRegion(baseregion, -1, maxz+1, 0); return *this * baseregpoly; } PMRegion PMRegion::createccdpoly() { return createccdpoly(traversedarea()); } /* Calculate a complementary coverduration polyhedron from a polyhedron * representing a moving region related to a given baseregion. */ PMRegion PMRegion::createccdpoly(RList baseregion) { // First, create a coverduration polyhedron PMRegion cdpoly = createcdpoly(); // Then extrude the base region to the height of the cdpolyhedron // (and slightly above, to keep the polyhedron simple) Kernel::FT maxz = cdpoly.minmaxz().second + 1; PMRegion baseregpoly = PMRegion::fromRegion(baseregion, 0, maxz, 0); return baseregpoly - cdpoly; // Create the intersection between the cdpolyhedron and the extruded // base region PMRegion tmp = cdpoly * baseregpoly; // and finally return the difference between the baseregion polyhedron // and the restricted cdpolyhedron return baseregpoly - tmp; } /* Calculate an interval coverduration polyhedron from a polyhedron * representing a moving region related to a given baseregion. */ PMRegion PMRegion::createicdpoly(Kernel::FT duration, RList baseregion) { // First, create a coverduration polyhedron restricted // to the given baseregion PMRegion cdpoly = createcdpoly(baseregion); // Copy the cdpolyhedron and translate it according to the given duration PMRegion tmp; tmp.polyhedron = cdpoly.polyhedron; tmp.translate(0, 0, -duration); // Subtract the cdpolyhedron with the translated version of itself return cdpoly - tmp; } /* Calculate an interval coverduration polyhedron from a polyhedron * representing a moving region. */ PMRegion PMRegion::createicdpoly(Kernel::FT duration) { // First, create a coverduration polyhedron PMRegion cdpoly = createcdpoly(); // Copy the cdpolyhedron and translate it according to the given duration PMRegion tmp; tmp.polyhedron = cdpoly.polyhedron; tmp.translate(0, 0, -duration); // Subtract the cdpolyhedron with the translated version of itself return cdpoly - tmp; } // The next three functions do all the same, the only difference is, on // which type of coverduration polyhedron (cd, ccd or icd) they are // applied. /* This function operates on a coverduration polyhedron (cdpolyhedron) and returns the area, which is covered at least for the given duration */ RList PMRegion::coveredlonger(Kernel::FT duration) { return atinstant(duration); } /* This function operates on a complementary coverduration polyhedron * (ccdpolyhedron) and returns the area, which is covered at most for the given duration */ RList PMRegion::coveredshorter(Kernel::FT duration) { return atinstant(duration); } /* This function operates on an interval coverduration polyhedron (icdpolyhedron) and returns the area, which is covered at least for the given duration and at most for duration + t, where t is the duration specified when the icdpolyhedron was created. */ RList PMRegion::intervalcovered(Kernel::FT duration) { return atinstant(duration); } /* This function calculates the average cover time of the (moving) region */ Kernel::FT PMRegion::avgcover() { // Calculate the volume of the cdpolyhedron Kernel::FT v = CGAL::Polygon_mesh_processing::volume(polyhedron); // Calculate the area of the "traversed area" Kernel::FT a = ::area(projectxy()); return v/a; } /* This function calculates the average cover time of the given base region by the (moving) region. The same base region must have been specified when creating the cdpolyhedron. */ Kernel::FT PMRegion::avgcover(RList basereg) { // Calculate the volume of the cdpolyhedron Kernel::FT v = CGAL::Polygon_mesh_processing::volume(polyhedron); // Calculate the area of the base region Kernel::FT a = ::area(Region2Polygons(basereg)); // The average cover duration is the volume divided by the area return v/a; } } #endif /* PMREGION_DISABLE_COVERDURATION */