// PROTECTED METHOD /// <summary> /// Construct voronoi face based on Delaunay triangulation. Vertices at infinity /// are define based on radius parameter. It should be large enough to avoid /// some circumcenters (finite voronoi vertices) to be further on. /// </summary> /// <remarks> /// Each face is yield just after their construction. Then it's neighborhood /// is not guarantee to be constructed. /// </remarks> /// <param name="radius">Distance used to construct site that are at infinity.</param> protected IEnumerable <Face <TEdge, TFace> > ExportFaces(Func <Vec3, Vec3, Vec3, Vec3> centerCalculator, double radius) { // FIFO var queue = new Queue <QuadEdge <TEdge> >(); // Start at the far left QuadEdge <TEdge> first = _mesh.LeftMostEdge; // @TODO Bounds List <QuadEdge <TEdge> > bounds = new List <QuadEdge <TEdge> >(); // Visit all edge of the convex hull to compute dual vertices // at infinity by looping in a CW order over edges with same left face. foreach (QuadEdge <TEdge> hullEdge in first.LeftEdges(CCW:false)) { // Construct a new face // First infinite voronoi vertex if (hullEdge.Rot.Destination == null) { hullEdge.Rot.Destination = ConstructAtInfinity(hullEdge.Sym, radius, centerCalculator); } // Add other vertices by looping over hullEdge origin in CW order (Oprev) foreach (QuadEdge <TEdge> current in hullEdge.EdgesFrom(CCW:false)) { if (current.Rot.Origin == null) { // Delaunay edge on the boundary if (Geometry.LeftOf(current.Oprev.Destination, current)) { current.Rot.Origin = ConstructAtInfinity(current, radius, centerCalculator); } else { current.Rot.Origin = centerCalculator(current.Origin, current.Destination, current.Oprev.Destination); // Speed up computation of point coordinates // All edges sharing the same origin should have same // geometrical origin foreach (QuadEdge <TEdge> otherDual in current.Rot.EdgesFrom()) { otherDual.Origin = current.Rot.Origin; } } } if (current.Sym.Tag == _mesh.VisitedTagState) { queue.Enqueue(current.Sym); bounds.Add(current.Sym); } current.Tag = !_mesh.VisitedTagState; } // After face construction over yield return(new Face <TEdge, TFace>(hullEdge, true, true)); } // Convex hull now closed --> Construct bounded voronoi faces while (queue.Count > 0) { QuadEdge <TEdge> edge = queue.Dequeue(); if (edge.Tag == _mesh.VisitedTagState) { // Construct a new face foreach (QuadEdge <TEdge> current in edge.EdgesFrom(CCW:false)) { if (current.Rot.Origin == null) { current.Rot.Origin = centerCalculator(current.Origin, current.Destination, current.Oprev.Destination); // Speed up computation of point coordinates // All edges sharing the same origin have same // geometrical origin foreach (QuadEdge <TEdge> otherDual in current.Rot.EdgesFrom()) { otherDual.Origin = current.Rot.Origin; } } if (current.Sym.Tag == _mesh.VisitedTagState) { queue.Enqueue(current.Sym); } current.Tag = !_mesh.VisitedTagState; } // After face construction over if (bounds.Contains(edge)) { yield return(new Face <TEdge, TFace>(edge, true, false)); } else { yield return(new Face <TEdge, TFace>(edge, false, false)); } } } // Inverse flag to be able to traverse again at next call _mesh.SwitchInternalFlag(); }
/// <summary> /// Construct voronoi face based on Delaunay triangulation. Vertices at infinity /// are define based on radius parameter. It should be large enough to avoid /// some circumcenters (finite voronoi vertices) to be further on. /// </summary> /// <remarks> /// Each face is yield just after their construction. Then it's neighborhood /// is not guarantee to be constructed. /// </remarks> protected IEnumerable <Face <TEdge, TFace> > ExportFaces(Func <Vec3, Vec3, Vec3, Vec3> centerCalculator, double scaleFactor) { // FIFO var queue = new Queue <QuadEdge <TEdge> >(); // Start at the far left QuadEdge <TEdge> first = LeftMostEdge; // @TODO Make sure CyclingMerge compute a valide triangulation // Construct first face using Centroid because // triangulation is not necessary delaunay foreach (QuadEdge <TEdge> current in first.EdgesFrom(CCW:false)) { if (current.Rot.Origin == null) { current.Rot.Origin = Geometry.Centroid(Geometry.InvStereographicProjection(current.Origin), Geometry.InvStereographicProjection(current.Destination), Geometry.InvStereographicProjection(current.Oprev.Destination)); double invDistanceScaled = scaleFactor / current.Rot.Origin.Magnitude; current.Rot.Origin *= invDistanceScaled; // Speed up computation of point coordinates // All edges sharing the same origin have same // geometrical origin foreach (QuadEdge <TEdge> otherDual in current.Rot.EdgesFrom()) { otherDual.Origin = current.Rot.Origin; } } if (current.Sym.Tag == _mesh.VisitedTagState) { queue.Enqueue(current.Sym); } current.Tag = !_mesh.VisitedTagState; } yield return(new Face <TEdge, TFace>(first, false, false)); // Convex hull now closed --> Construct bounded voronoi faces while (queue.Count > 0) { QuadEdge <TEdge> edge = queue.Dequeue(); if (edge.Tag == _mesh.VisitedTagState) { // Construct a new face foreach (QuadEdge <TEdge> current in edge.EdgesFrom(CCW:false)) { if (current.Rot.Origin == null) { current.Rot.Origin = centerCalculator(Geometry.InvStereographicProjection(current.Origin), Geometry.InvStereographicProjection(current.Destination), Geometry.InvStereographicProjection(current.Oprev.Destination)); double invDistanceScaled = scaleFactor / current.Rot.Origin.Magnitude; current.Rot.Origin *= invDistanceScaled; // Speed up computation of point coordinates // All edges sharing the same origin have same // geometrical origin foreach (QuadEdge <TEdge> otherDual in current.Rot.EdgesFrom()) { otherDual.Origin = current.Rot.Origin; } } if (current.Sym.Tag == _mesh.VisitedTagState) { queue.Enqueue(current.Sym); } current.Tag = !_mesh.VisitedTagState; } yield return(new Face <TEdge, TFace>(edge, false, false)); } } // Inverse flag to be able to traverse again at next call _mesh.SwitchInternalFlag(); }