#include "MotorcycleGraph.h" #include #include #include #include #include #include #include #include "MotorcycleOptionsInPatch.h" #include "ParametrizationHelper.h" #include "PatchSplittingProblem.h" // ------------ Motorcycle ------------ void MotorcycleGraph::Motorcycle::AddToPath(HEMesh::HalfedgeHandle h, const HEMesh& mesh) { path.push_back(h); if (!mesh.is_boundary(h) && !mesh.is_boundary(mesh.opposite_halfedge_handle(h))) ++nonBoundaryEdges; } void MotorcycleGraph::Motorcycle::RemoveLastPathSegment(const HEMesh& mesh) { if (!mesh.is_boundary(path.back()) && !mesh.is_boundary(mesh.opposite_halfedge_handle(path.back()))) --nonBoundaryEdges; path.pop_back(); } // ------------ MotorcycleStop ------------ MotorcycleGraph::MotorcycleStop::MotorcycleStop(size_t motorcycleId, size_t locationInMotorcyclePath) : motorcycle(motorcycleId), locationInMotorcyclePath(locationInMotorcyclePath) { } // ------------ LocationOnPath ------------ MotorcycleGraph::LocationOnPath::LocationOnPath() { } MotorcycleGraph::LocationOnPath::LocationOnPath(size_t motorcycle, size_t pathSegment, bool goForward) : motorcycle(motorcycle), pathSegment(pathSegment), goForward(goForward) { } bool MotorcycleGraph::LocationOnPath::operator==(const LocationOnPath& other) const { return motorcycle == other.motorcycle && pathSegment == other.pathSegment && goForward == other.goForward; } // ------------ ExtractionStatistics ------------ void MotorcycleGraph::ExtractionStatistics::Clear() { totalFaces = 0; openPatches = 0; maxPatchDegree = 0; patchCountPerNumberOfCorners.clear(); patchSizesPerNumberOfCorners.clear(); patchSizes.clear(); } std::ostream& operator<<(std::ostream& stream, const MotorcycleGraph::ExtractionStatistics& s) { std::cout << s.openPatches << " open patches." << std::endl; for (int corners = 0; corners < s.patchCountPerNumberOfCorners.size(); ++corners) std::cout << corners << " corners: " << (100.0f * s.patchSizesPerNumberOfCorners[corners] / s.totalFaces) << "% (" << s.patchCountPerNumberOfCorners[corners] << " patches)" << std::endl; size_t steps[] = { 0, 1, 10, 50, 100, 500, 1000, 5000, 10000 }; size_t stepCount = sizeof(steps) / sizeof(steps[0]); for (int i = 0; i <= stepCount; ++i) { auto lower = (i == 0 ? s.patchSizes.begin() : std::upper_bound(s.patchSizes.begin(), s.patchSizes.end(), steps[i - 1])); auto upper = (i == stepCount ? s.patchSizes.end() : std::upper_bound(s.patchSizes.begin(), s.patchSizes.end(), steps[i])); size_t faces = 0; for (auto it = lower; it != upper; ++it) faces += *it; if (i < stepCount) std::cout << "Faces in patches with <= " << steps[i]; else std::cout << "Faces in patches with > " << steps[i - 1]; std::cout << " faces: " << 100.0f * (float)faces / s.totalFaces << "% (" << (upper - lower) << " patches)" << std::endl; } return stream; } // ------------ HalfArc ------------ MotorcycleGraph::HalfArc::Segment::Segment(const LocationOnPath& location, size_t length) : location(location), length(length) { } MotorcycleGraph::HalfArc::HalfArc(const PathSegmentIterator& first, const PathSegmentIterator& last) { segments.emplace_back(*first, 0); if (first == last) return; auto it = first; ++it; int length = 1; //Find the segments of equal motorcycles that make up the given range while (it != last) { auto newLocation = *it; if (newLocation.motorcycle != segments.back().location.motorcycle) { segments.back().length = length; segments.emplace_back(newLocation, 0); length = 1; } else ++length; ++it; } segments.back().length = length; } size_t MotorcycleGraph::HalfArc::Length() const { size_t l = 0; for (auto& segment : segments) l += segment.length; return l; } void MotorcycleGraph::HalfArc::MergeWith(HalfArc&& merged) { segments.insert(segments.end(), merged.segments.begin(), merged.segments.end()); merged.segments.clear(); } MotorcycleGraph::LocationOnPath MotorcycleGraph::HalfArc::LastPathSegment() const { auto& segment = segments.back(); if (segment.location.goForward) return LocationOnPath(segment.location.motorcycle, segment.location.pathSegment + segment.length - 1, segment.location.goForward); else return LocationOnPath(segment.location.motorcycle, segment.location.pathSegment - segment.length + 1, segment.location.goForward); } void MotorcycleGraph::HalfArc::SetStartInclusive(const PathSegmentIterator& start) { assert(start.arc == this); auto newStartLocation = *start; segments.erase(segments.begin(), segments.begin() + start.iMotorcycle); segments.front().location = newStartLocation; segments.front().length -= start.iPathSegmentOnMotorcycle; } void MotorcycleGraph::HalfArc::SetEndExclusive(const PathSegmentIterator& end) { assert(end.arc == this); if (end.iPathSegmentOnMotorcycle == 0) { segments.erase(segments.begin() + end.iMotorcycle, segments.end()); return; } segments.erase(segments.begin() + end.iMotorcycle + 1, segments.end()); segments.back().length = end.iPathSegmentOnMotorcycle; } MotorcycleGraph::HalfArc::PathSegmentIterator MotorcycleGraph::HalfArc::begin() const { return PathSegmentIterator(0, 0, this); } MotorcycleGraph::HalfArc::PathSegmentIterator MotorcycleGraph::HalfArc::end() const { return PathSegmentIterator(segments.size(), 0, this); } // ------------ HalfArc::PathSegmentIterator ------------ MotorcycleGraph::HalfArc::PathSegmentIterator::PathSegmentIterator(int iMotorcycle, int iPathSegment, const HalfArc* arc) : iMotorcycle(iMotorcycle), iPathSegmentOnMotorcycle(iPathSegment), arc(arc) { } bool MotorcycleGraph::HalfArc::PathSegmentIterator::operator==(const PathSegmentIterator& other) const { return iMotorcycle == other.iMotorcycle && iPathSegmentOnMotorcycle == other.iPathSegmentOnMotorcycle; } bool MotorcycleGraph::HalfArc::PathSegmentIterator::operator!=(const PathSegmentIterator& other) const { return !(*this == other); } MotorcycleGraph::LocationOnPath MotorcycleGraph::HalfArc::PathSegmentIterator::operator*() const { auto& segment = arc->segments[iMotorcycle]; if (segment.location.goForward) return LocationOnPath(segment.location.motorcycle, segment.location.pathSegment + iPathSegmentOnMotorcycle, segment.location.goForward); else return LocationOnPath(segment.location.motorcycle, segment.location.pathSegment - iPathSegmentOnMotorcycle, segment.location.goForward); } MotorcycleGraph::HalfArc::PathSegmentIterator& MotorcycleGraph::HalfArc::PathSegmentIterator::operator++() { ++iPathSegmentOnMotorcycle; if (iPathSegmentOnMotorcycle >= arc->segments[iMotorcycle].length) { iPathSegmentOnMotorcycle = 0; ++iMotorcycle; } return *this; } // ------------ MotorcycleGraph ------------ MotorcycleGraph::MotorcycleGraph(const HEMesh& mesh, PatchSet& patchSet, const std::vector& metaSingularities, const ManifoldnessAwareVertexMap& vertexToMetaSingularityIndex, const OpenMesh::EPropHandleT isOriginalEdgeProp, LengthMeasure lengthMeasure) : mesh(&mesh), patchSet(&patchSet), metaSingularities(metaSingularities), vertexToMetaSingularityIndex(vertexToMetaSingularityIndex), visitedVertices(mesh), isOriginalEdgeProp(isOriginalEdgeProp), lengthMeasure(lengthMeasure) { } size_t MotorcycleGraph::OppositeHalfarc(size_t arc) const { return arc ^ 1; } size_t MotorcycleGraph::AddMotorcycle(HEMesh::HalfedgeHandle h, bool highPriority) { auto& visitedAtSource = visitedVertices.AccessOrCreateAtToVertex(mesh->opposite_halfedge_handle(h)); int mergeWith = -1; if (visitedHalfEdges.find(h) != visitedHalfEdges.end()) { //The halfedge has already been visited auto oppH = mesh->opposite_halfedge_handle(h); for (auto& stop : visitedAtSource) { //if the other motorcycle is terminated here, we can just merge with it auto& motorcycle = motorcycles[stop.motorcycle]; if (motorcycle.terminated && motorcycle.straightContinuationEnd == (size_t)-1 && motorcycle.Path().back() == oppH) mergeWith = stop.motorcycle; } if (mergeWith == -1) { std::cout << "The halfedge "; PrintFullHalfedge(std::cout, h, *mesh); std::cout << " has already been used." << std::endl; throw std::runtime_error("Error adding a motorcycle"); } } motorcycles.emplace_back(h, highPriority); if (highPriority) hasHighPriorityMotorcycles = true; if (mergeWith != -1) { motorcycles[motorcycles.size() - 1].straightContinuationEnd = mergeWith; motorcycles[mergeWith].straightContinuationEnd = motorcycles.size() - 1; motorcycles[motorcycles.size() - 1].terminated = true; } else activeMotorcycles.push_back(motorcycles.size() - 1); //Record the collision for all motorcycles passing through the start vertex for (auto& stop : visitedAtSource) motorcycles[stop.motorcycle].crashesOnPath.insert(stop.locationInMotorcyclePath); visitedAtSource.emplace_back(motorcycles.size() - 1, 0); visitedHalfEdges.insert(h); return motorcycles.size() - 1; } size_t MotorcycleGraph::AddMotorcyclePair(const std::array& h, bool highPriority) { #if __DEBUG assert(mesh->from_vertex_handle(h[0]) == mesh->from_vertex_handle(h[1])); #endif auto& visitedAtSource = visitedVertices.AccessOrCreateAtToVertex(mesh->opposite_halfedge_handle(h[0])); int mergeH0With = -1, mergeH1With = -1; if (visitedHalfEdges.find(h[0]) != visitedHalfEdges.end()) { //The halfedge has already been visited auto oppH0 = mesh->opposite_halfedge_handle(h[0]); for (auto& stop : visitedAtSource) { //if the other motorcycle is terminated here, we can just merge with it auto& motorcycle = motorcycles[stop.motorcycle]; if (motorcycle.terminated && motorcycle.straightContinuationEnd == (size_t)-1 && motorcycle.Path().back() == oppH0) mergeH0With = stop.motorcycle; } if(mergeH0With == -1) std::cout << "The first halfedge at " << mesh->point(mesh->from_vertex_handle(h[0])) << " of the motorcycle pair has already been used." << std::endl; } if (visitedHalfEdges.find(h[1]) != visitedHalfEdges.end()) { //The halfedge has already been visited auto oppH1 = mesh->opposite_halfedge_handle(h[1]); for (auto& stop : visitedAtSource) { //if the other motorcycle is terminated here, we can just merge with it auto& motorcycle = motorcycles[stop.motorcycle]; if (motorcycle.terminated && motorcycle.straightContinuationEnd == (size_t)-1 && motorcycle.Path().back() == oppH1) mergeH1With = stop.motorcycle; } if (mergeH1With == -1) std::cout << "The second halfedge at " << mesh->point(mesh->from_vertex_handle(h[1])) << " of the motorcycle pair has already been used." << std::endl; } for (auto& stop : visitedAtSource) motorcycles[stop.motorcycle].crashesOnPath.insert(stop.locationInMotorcyclePath); for (int i = 0; i < 2; ++i) { motorcycles.emplace_back(h[i], highPriority); visitedAtSource.emplace_back(motorcycles.size() - 1, 0); visitedHalfEdges.insert(h[i]); } if (highPriority) hasHighPriorityMotorcycles = true; motorcycles[motorcycles.size() - 2].straightContinuationStart = motorcycles.size() - 1; motorcycles[motorcycles.size() - 1].straightContinuationStart = motorcycles.size() - 2; if (mergeH0With != -1) { motorcycles[motorcycles.size() - 2].straightContinuationEnd = mergeH0With; motorcycles[mergeH0With].straightContinuationEnd = motorcycles.size() - 2; motorcycles[motorcycles.size() - 2].terminated = true; } else activeMotorcycles.push_back(motorcycles.size() - 2); if (mergeH1With != -1) { motorcycles[motorcycles.size() - 1].straightContinuationEnd = mergeH1With; motorcycles[mergeH1With].straightContinuationEnd = motorcycles.size() - 1; motorcycles[motorcycles.size() - 1].terminated = true; } else activeMotorcycles.push_back(motorcycles.size() - 1); return motorcycles.size() - 2; } void MotorcycleGraph::AddMotorcycleWithHistory(const std::vector& path, bool highPriority, bool collideWithBoundary) { if (path.size() == 0) return; if (visitedHalfEdges.find(path[0]) != visitedHalfEdges.end()) return; //already used the beginning of this path motorcycles.emplace_back(); motorcycles.back().highPriority = highPriority; if (highPriority) hasHighPriorityMotorcycles = true; auto& sourceVisited = visitedVertices.AccessOrCreateAtToVertex(mesh->opposite_halfedge_handle(path[0])); for (auto& stop : sourceVisited) motorcycles[stop.motorcycle].crashesOnPath.insert(stop.locationInMotorcyclePath); sourceVisited.emplace_back(motorcycles.size() - 1, motorcycles.back().Path().size()); bool active = !collideWithBoundary; for(auto it = path.begin(); it < path.end(); ++it) { //The last edge is only used if the motorcycle will immediately terminate. Otherwise, //it just sits at the edge. bool useEdge = it != path.end() - 1 || !active; auto inserted = visitedHalfEdges.insert(*it); if (!inserted.second) { //the motorcycle already collided with another motorcycle std::cout << "This should not happen." << std::endl; } if (useEdge) { OccupyEdge(mesh->edge_handle(*it), motorcycles.size() - 1); visitedHalfEdges.insert(mesh->opposite_halfedge_handle(*it)); motorcycles.back().AddToPath(*it, *mesh); auto& targetVisited = visitedVertices.AccessOrCreateAtToVertex(*it); for (auto& stop : targetVisited) motorcycles[stop.motorcycle].crashesOnPath.insert(stop.locationInMotorcyclePath); targetVisited.emplace_back(motorcycles.size() - 1, motorcycles.back().Path().size()); if (targetVisited.size() > 1) { active = false; //check for head-on collision for (auto& stop : targetVisited) { if (motorcycles[stop.motorcycle].currentPosition == mesh->opposite_halfedge_handle(*it)) { //this is a head-on collision motorcycles.back().straightContinuationEnd = stop.motorcycle; motorcycles[stop.motorcycle].straightContinuationEnd = motorcycles.size() - 1; motorcycles[stop.motorcycle].terminated = true; break; } } break; } } else motorcycles.back().currentPosition = *it; } if (active) activeMotorcycles.push_back(motorcycles.size() - 1); else motorcycles.back().terminated = true; if(collideWithBoundary) motorcycles.back().collideWithPaddedBoundary = true; } bool MotorcycleGraph::HasActiveMotorcycles() const { return !activeMotorcycles.empty(); } const std::vector& MotorcycleGraph::Patches() const { return patches; } void MotorcycleGraph::FindPossibleExits(const FencedRegion& patch, const std::pair& enteringInfo, std::set& possibleExits, bool verbose) { auto& loop = patch.Loops()[0]; //Walk the fence and record the edges that result in a zero-turn traversal. //Motorcycles that pass through the region are considered boundaries. The edge through which //the motorcycles enters is marked with -2 turns. //The orientation of the current edge on the fence. Can be -1 if we are //currently not on the boundary but on a motorcycle. int currentRegionEdgeOrientation = enteringInfo.second.enteringViaEdgeColor; //The current motorcycle that we are travelling on. currentMotorcycle.motorcycle can be //(size_t)-1 if we are currently not on a motorcycle but on the fence. LocationOnPath currentMotorcycle; currentMotorcycle.motorcycle = (size_t)-1; //Records the number of turns needed to traverse through the fenced region leaving at the //current position. int currentTurns = -2; HEMesh::HalfedgeHandle currentH; if (!mesh->is_boundary(enteringInfo.first)) //find the halfedge that is to the left of the entry point currentH = mesh->prev_halfedge_handle(enteringInfo.first); else { //if we are entering through a boundary edge, the halfedge to the left //of the entry point does not exist. Instead, we use the inverse of the //entering edge, which has a turn count of -3 instead of -2. currentH = mesh->opposite_halfedge_handle(enteringInfo.first); currentRegionEdgeOrientation = (currentRegionEdgeOrientation + loop.Degree() - 1) % loop.Degree(); currentTurns = -3; } { //Check if we are also currently on a motorcycle VisitedEntry* visited; if(visitedVertices.TryAccessAtToVertex(mesh->opposite_halfedge_handle(currentH), visited)) currentMotorcycle = EdgeToMotorcycleLocation(currentH, *visited); } auto startVertex = mesh->from_vertex_handle(enteringInfo.first); if (verbose) { std::cout << "Entering patch at "; PrintFullHalfedge(std::cout, enteringInfo.first, *mesh); std::cout << ", initial halfedge: "; PrintFullHalfedge(std::cout, currentH, *mesh); std::cout << ", currentQuadPatchEdgeColor: " << currentTurns << std::endl; } int outerIterations = 0; bool hIsInitialEdge = true; //Move along the boundary (or motorcycles) do { if (outerIterations++ > 1000) { std::cout << "Problem with outer iteration when entering patch at " << mesh->point(startVertex) << std::endl; throw std::runtime_error("Unknown error while finding appropriate exit points"); } bool currentIsPaddedBorder = mesh->is_boundary(mesh->opposite_halfedge_handle(currentH)) && currentRegionEdgeOrientation != -1; //there will never be a motorcycle on the padded boundary //DEBUG: if (currentIsPaddedBorder && currentMotorcycle.motorcycle != (size_t)-1) throw std::runtime_error("There was a motorcycle on the padded boundary."); int iterations = 0; int nextRegionEdgeOrientation = -1; LocationOnPath nextMotorcycle; nextMotorcycle.motorcycle = (size_t)-1; VisitedEntry* visited; auto isVisited = visitedVertices.TryAccessAtToVertex(currentH, visited); int hopsFromCurrentToNext = 0; int boundaryOrientationChange = 0; //Find the edge onto which to continue auto stopCondition = [&](HEMesh::HalfedgeHandle h) { if (iterations++ > 10) { std::cout << "Problem with inner iteration when entering patch at " << mesh->point(mesh->from_vertex_handle(enteringInfo.first)) << std::endl; throw std::runtime_error("Unknown error while finding appropriate exit points"); } if (verbose) { std::cout << "Checking halfedge "; PrintFullHalfedge(std::cout, h, *mesh); std::cout << std::endl; } ++hopsFromCurrentToNext; boundaryOrientationChange = 0; //check if we continue on the fence bool currentHOnPaddedBoundary = false; auto edgeOnFence = patch.Loops()[0].FindEdge(h); if (edgeOnFence != (size_t)-1) { nextRegionEdgeOrientation = patch.Loops()[0].Edges()[edgeOnFence].orientation; if (mesh->is_boundary(mesh->opposite_halfedge_handle(h))) { currentHOnPaddedBoundary = true; if (currentMotorcycle.motorcycle != (size_t)-1) { //check if this is a valid transition if (!IsValidTransitionFromMotorcycleToBoundary(currentMotorcycle, currentH, *visited, true, boundaryOrientationChange, verbose) && !patch.IsToVertexOnBoundary(mesh->opposite_halfedge_handle(h), true)) { nextRegionEdgeOrientation = -1; currentHOnPaddedBoundary = false; } else if (boundaryOrientationChange == 0) boundaryOrientationChange = 2; } } } //check if we continue on a motorcycle if (isVisited) { nextMotorcycle = EdgeToMotorcycleLocation(h, *visited); if (nextMotorcycle.motorcycle != (size_t)-1) { if (currentIsPaddedBorder) { //check if this is a valid transition auto copy = nextMotorcycle; copy.goForward = !copy.goForward; if (!IsValidTransitionFromMotorcycleToBoundary(copy, mesh->opposite_halfedge_handle(currentH), *visited, false, boundaryOrientationChange, verbose) && !patch.IsToVertexOnBoundary(mesh->opposite_halfedge_handle(h), true)) nextMotorcycle.motorcycle = (size_t)-1; else if (boundaryOrientationChange == 0) boundaryOrientationChange = 2; } if (currentMotorcycle.motorcycle == nextMotorcycle.motorcycle && currentMotorcycle.goForward != nextMotorcycle.goForward) nextMotorcycle.motorcycle = (size_t)-1; } if (nextMotorcycle.motorcycle != (size_t)-1 && currentHOnPaddedBoundary) nextRegionEdgeOrientation = -1; } return nextRegionEdgeOrientation != -1 || nextMotorcycle.motorcycle != (size_t)-1; }; HEMesh::HalfedgeHandle nextH; if (stopCondition(mesh->opposite_halfedge_handle(currentH))) { nextH = mesh->opposite_halfedge_handle(currentH); hopsFromCurrentToNext = 0; } else { hopsFromCurrentToNext = 0; nextH = currentH; CirculateForwardUntil(nextH, *mesh, stopCondition); } bool straightContinuation = false; //Find if the current transition is a straight continuation if (!hIsInitialEdge) { if (currentMotorcycle.motorcycle != (size_t)-1 && nextMotorcycle.motorcycle == (size_t)-1) { if (!motorcycles[currentMotorcycle.motorcycle].terminated && motorcycles[currentMotorcycle.motorcycle].currentPosition == nextH) //Although the motorcycle did not yet go there, this is in fact a straight continuation. If we don't //do this, this location will be counted as a transition from motorcycle to edge straightContinuation = true; } if (nextMotorcycle.motorcycle != (size_t)-1 && currentMotorcycle.motorcycle == (size_t)-1) { if (!motorcycles[nextMotorcycle.motorcycle].terminated && mesh->opposite_halfedge_handle(motorcycles[nextMotorcycle.motorcycle].currentPosition) == currentH) //Same as above, just in the other direction straightContinuation = true; } } else { hIsInitialEdge = false; } if (currentIsPaddedBorder && currentMotorcycle.motorcycle == (size_t)-1 && nextMotorcycle.motorcycle != (size_t)-1) { bool collisionWithPaddedBoundary = (motorcycles[nextMotorcycle.motorcycle].startInPaddedBoundary && mesh->edge_handle(nextH) == mesh->edge_handle(motorcycles[nextMotorcycle.motorcycle].Path()[0])) || (motorcycles[nextMotorcycle.motorcycle].collideWithPaddedBoundary && mesh->edge_handle(nextH) == mesh->edge_handle(motorcycles[nextMotorcycle.motorcycle].Path()[motorcycles[nextMotorcycle.motorcycle].Path().size() - 1])); if (!collisionWithPaddedBoundary) ++currentTurns; //two corners collapsed into one (the other one will be counted by the usual check) } if (mesh->is_boundary(mesh->opposite_halfedge_handle(nextH)) && nextRegionEdgeOrientation != -1 && currentMotorcycle.motorcycle != (size_t)-1 && nextMotorcycle.motorcycle == (size_t)-1) { bool collisionWithPaddedBoundary = (motorcycles[currentMotorcycle.motorcycle].startInPaddedBoundary && mesh->edge_handle(currentH) == mesh->edge_handle(motorcycles[currentMotorcycle.motorcycle].Path()[0])) || (motorcycles[currentMotorcycle.motorcycle].collideWithPaddedBoundary && mesh->edge_handle(currentH) == mesh->edge_handle(motorcycles[currentMotorcycle.motorcycle].Path()[motorcycles[currentMotorcycle.motorcycle].Path().size() - 1])); if (!collisionWithPaddedBoundary) ++currentTurns; //two corners collapsed into one (the other one will be counted by the usual check) } if ((nextMotorcycle.motorcycle == currentMotorcycle.motorcycle && nextMotorcycle.motorcycle != (size_t)-1) || (nextRegionEdgeOrientation == currentRegionEdgeOrientation && nextRegionEdgeOrientation != -1)) straightContinuation = true; auto findStraightContinuation = [&](size_t motorcycle1, size_t motorcycle2) { if (motorcycle1 != -1) { auto continuation = FindStraightContinuation(motorcycle1, true); if (continuation != (size_t)-1 && motorcycle2 == continuation) straightContinuation = true; continuation = FindStraightContinuation(motorcycle1, false); if (continuation != (size_t)-1 && motorcycle2 == continuation) straightContinuation = true; else if (continuation != (size_t)-1 && motorcycles[continuation].Path().size() == 0 && !motorcycles[continuation].terminated) { //the continuation motorcycle might still be at its start with no path segments at all auto continuationEdge = mesh->edge_handle(motorcycles[continuation].currentPosition); if (mesh->edge_handle(nextH) == continuationEdge) straightContinuation = true; if (mesh->edge_handle(currentH) == continuationEdge) straightContinuation = true; } } }; if (!straightContinuation) findStraightContinuation(currentMotorcycle.motorcycle, nextMotorcycle.motorcycle); if (!straightContinuation) findStraightContinuation(nextMotorcycle.motorcycle, currentMotorcycle.motorcycle); //Update the current turn count for the current transition if (boundaryOrientationChange != 0) currentTurns += boundaryOrientationChange; else if (straightContinuation) ; //straight continuation else if (currentRegionEdgeOrientation != -1 && nextRegionEdgeOrientation == -1) ++currentTurns; //switch from fence to motorcycle else if (currentRegionEdgeOrientation == -1 && nextRegionEdgeOrientation != -1) ++currentTurns; //switch from motorcycle to fence else if (currentRegionEdgeOrientation != -1 && nextRegionEdgeOrientation != -1 && currentRegionEdgeOrientation != nextRegionEdgeOrientation) currentTurns += 2 - hopsFromCurrentToNext; else if (currentMotorcycle.motorcycle != nextMotorcycle.motorcycle) ++currentTurns; //switch from one motorcycle to another if (verbose) std::cout << "Continued to vertex " << mesh->to_vertex_handle(nextH) << " with regionEdgeColor = " << nextRegionEdgeOrientation << ", motorcycle = " << nextMotorcycle.motorcycle << ", quadPatchColor = " << currentTurns << std::endl; if (currentTurns == 0) { //The current edge is a valid exit point possibleExits.insert(nextH); int checkMotorcycle = -1; HEMesh::HalfedgeHandle edgeOnMotorcycle; if (currentRegionEdgeOrientation != -1 && mesh->is_boundary(mesh->opposite_halfedge_handle(currentH)) && nextMotorcycle.motorcycle != (size_t)-1 && currentMotorcycle.motorcycle == (size_t)-1) { checkMotorcycle = nextMotorcycle.motorcycle; edgeOnMotorcycle = nextH; } if (nextRegionEdgeOrientation != -1 && mesh->is_boundary(mesh->opposite_halfedge_handle(nextH)) && currentMotorcycle.motorcycle != (size_t)-1 && nextMotorcycle.motorcycle == (size_t)-1) { checkMotorcycle = currentMotorcycle.motorcycle; edgeOnMotorcycle = nextH; } if (checkMotorcycle != -1) { //Also add the adjacent edge as target (even if it is technically not reachable). This is done //because the tracer checks both sides when colliding with a motorcycle for (auto& stop : visitedVertices.AccessAtToVertex(currentH)) { if (stop.motorcycle == checkMotorcycle) { if (stop.locationInMotorcyclePath > 0 && stop.locationInMotorcyclePath < motorcycles[checkMotorcycle].Path().size() - 1) { if (motorcycles[checkMotorcycle].Path()[stop.locationInMotorcyclePath - 1] == edgeOnMotorcycle || motorcycles[checkMotorcycle].Path()[stop.locationInMotorcyclePath] == edgeOnMotorcycle) { possibleExits.insert(motorcycles[checkMotorcycle].Path()[stop.locationInMotorcyclePath - 1]); possibleExits.insert(motorcycles[checkMotorcycle].Path()[stop.locationInMotorcyclePath]); } else { //use the inverse possibleExits.insert(mesh->opposite_halfedge_handle(motorcycles[checkMotorcycle].Path()[stop.locationInMotorcyclePath - 1])); possibleExits.insert(mesh->opposite_halfedge_handle(motorcycles[checkMotorcycle].Path()[stop.locationInMotorcyclePath])); } } } } } } currentRegionEdgeOrientation = nextRegionEdgeOrientation; currentMotorcycle = nextMotorcycle; currentH = nextH; } while (startVertex != mesh->to_vertex_handle(currentH)); } MotorcycleGraph::RouteResult MotorcycleGraph::RouteThroughPatch(HEMesh::HalfedgeHandle edge, const FencedRegion& patch, const std::pair& enteringInfo, bool desperate, bool verbose) { const std::pair attempts[] = { std::pair(0.3f, false), std::pair(-0.3f, false), std::pair(-0.5f, true), std::pair(-2.0f, true), }; if (patch.Loops().size() > 1) { std::cout << "Multi-loop patches not supported." << std::endl; throw 1; } auto& loop = patch.Loops()[0]; //Find an appropriate point of exit std::set possibleExits; //those are the edges perpendicular to the motorcycle's exit direction FindPossibleExits(patch, enteringInfo, possibleExits, verbose); if (possibleExits.empty()) return RouteResult(NoRoute); for (int i = 0; i < (desperate ? 4 : 3); ++i) { auto result = RouteThroughPatch(edge, patch, attempts[i].first, attempts[i].second, possibleExits); if (result.type != NoRoute) return result; } return RouteResult(NoRoute); } MotorcycleGraph::RouteResult MotorcycleGraph::RouteThroughPatch(HEMesh::HalfedgeHandle edge, const FencedRegion& patch, const float minCosDeviationAngle, bool allowTurnsOnRegularVertices, const std::set& possibleExits) { //Represents a valid routing option struct Option { //cost of the route float cost; //route type RouteResultType type; //index of the corresponding motorcycle within options size_t cycleIdx; //if the route ends with a collision, this is the index of the motorcycle size_t collisionWith; //The first edge outside of the fenced region HEMesh::HalfedgeHandle edgeOutside; Option(float cost, RouteResultType type, size_t cycleIdx, size_t collisionWith, HEMesh::HalfedgeHandle edgeOutside) : cost(cost), type(type), cycleIdx(cycleIdx), collisionWith(collisionWith), edgeOutside(edgeOutside) { } bool operator<(const Option& other) const { if (cost != other.cost) return cost < other.cost; if (type != other.type) return type < other.type; if (cycleIdx != other.cycleIdx) return cycleIdx < other.cycleIdx; return false; } }; auto& loop = patch.Loops()[0]; std::set