123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- // -----------------------------------------------------------------------
- // <copyright file="SimpleSmoother.cs" company="">
- // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
- // </copyright>
- // -----------------------------------------------------------------------
- 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;
- /// <summary>
- /// Simple mesh smoother implementation.
- /// </summary>
- /// <remarks>
- /// Vertices wich should not move (e.g. segment vertices) MUST have a
- /// boundary mark greater than 0.
- /// </remarks>
- internal class SimpleSmoother : ISmoother
- {
- TrianglePool pool;
- Configuration config;
- IVoronoiFactory factory;
- ConstraintOptions options;
- /// <summary>
- /// Initializes a new instance of the <see cref="SimpleSmoother" /> class.
- /// </summary>
- public SimpleSmoother()
- : this(new VoronoiFactory())
- {
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="SimpleSmoother" /> class.
- /// </summary>
- 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 };
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="SimpleSmoother" /> class.
- /// </summary>
- /// <param name="factory">Voronoi object factory.</param>
- /// <param name="config">Configuration.</param>
- 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;
- }
- }
- }
- /// <summary>
- /// Calculate the centroid of a polygon.
- /// </summary>
- 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;
- }
- /// <summary>
- /// Rebuild the input geometry.
- /// </summary>
- 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<ISegment>());
- data.Holes.AddRange(mesh.holes);
- data.Regions.AddRange(mesh.regions);
- return data;
- }
- }
- }
|