// ----------------------------------------------------------------------- // // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ // // ----------------------------------------------------------------------- namespace UnityEngine.U2D.Animation.TriangleNet .Smoothing { using System.Linq; using Animation.TriangleNet.Geometry; using Animation.TriangleNet.Meshing; using Animation.TriangleNet.Topology.DCEL; using Animation.TriangleNet.Voronoi; /// /// Simple mesh smoother implementation. /// /// /// Vertices wich should not move (e.g. segment vertices) MUST have a /// boundary mark greater than 0. /// internal class SimpleSmoother : ISmoother { TrianglePool pool; Configuration config; IVoronoiFactory factory; ConstraintOptions options; /// /// Initializes a new instance of the class. /// public SimpleSmoother() : this(new VoronoiFactory()) { } /// /// Initializes a new instance of the class. /// public SimpleSmoother(IVoronoiFactory factory) { this.factory = factory; this.pool = new TrianglePool(); this.config = new Configuration( () => RobustPredicates.Default, () => pool.Restart()); this.options = new ConstraintOptions() { ConformingDelaunay = true }; } /// /// Initializes a new instance of the class. /// /// Voronoi object factory. /// Configuration. public SimpleSmoother(IVoronoiFactory factory, Configuration config) { this.factory = factory; this.config = config; this.options = new ConstraintOptions() { ConformingDelaunay = true }; } public void Smooth(IMesh mesh) { Smooth(mesh, 10); } public void Smooth(IMesh mesh, int limit) { var smoothedMesh = (Mesh)mesh; var mesher = new GenericMesher(config); var predicates = config.Predicates(); // The smoother should respect the mesh segment splitting behavior. this.options.SegmentSplitting = smoothedMesh.behavior.NoBisect; // Take a few smoothing rounds (Lloyd's algorithm). for (int i = 0; i < limit; i++) { Step(smoothedMesh, factory, predicates); // Actually, we only want to rebuild, if the mesh is no longer // Delaunay. Flipping edges could be the right choice instead // of re-triangulating... smoothedMesh = (Mesh)mesher.Triangulate(Rebuild(smoothedMesh), options); factory.Reset(); } smoothedMesh.CopyTo((Mesh)mesh); } private void Step(Mesh mesh, IVoronoiFactory factory, IPredicates predicates) { var voronoi = new BoundedVoronoi(mesh, factory, predicates); if (!voronoi.IsConsistent()) return; double x, y; foreach (var face in voronoi.Faces) { if (face.generator.label == 0) { Centroid(face, out x, out y); face.generator.x = x; face.generator.y = y; } } } /// /// Calculate the centroid of a polygon. /// private void Centroid(Face face, out double x, out double y) { double ai, atmp = 0, xtmp = 0, ytmp = 0; var edge = face.Edge; var first = edge.Next.ID; Point p, q; do { p = edge.Origin; q = edge.Twin.Origin; ai = p.x * q.y - q.x * p.y; atmp += ai; xtmp += (q.x + p.x) * ai; ytmp += (q.y + p.y) * ai; edge = edge.Next; } while (edge.Next.ID != first); x = xtmp / (3 * atmp); y = ytmp / (3 * atmp); //area = atmp / 2; } /// /// Rebuild the input geometry. /// private Polygon Rebuild(Mesh mesh) { var data = new Polygon(mesh.vertices.Count); foreach (var v in mesh.vertices.Values) { // Reset to input vertex. v.type = VertexType.InputVertex; data.Points.Add(v); } data.Segments.AddRange(mesh.subsegs.Values.Cast()); data.Holes.AddRange(mesh.holes); data.Regions.AddRange(mesh.regions); return data; } } }