using FeMM.Grasshopper.Helpers;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using System;
using System.Collections.Generic;

namespace FeMM.Grasshopper.Components.Patterning
{
    public class DeformArapComponent : GH_Component
    {
        /// <summary>
        /// Initializes a new instance of the UnwrapComponent class.
        /// </summary>
        public DeformArapComponent()
          : base("Deform ARAP", "DA", "Modify a mesh minimizing deformation", CategoryNameConstants.CATEGORY_FEMM, CategoryNameConstants.SUBCATEGORY_PATTERNING)
        {
        }

        /// <summary>
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            var idx = 0;
            pManager.AddMeshParameter("Mesh", "M", "The mesh to deform", GH_ParamAccess.item);
            pManager.AddIntegerParameter("Iterations", "I", "The number of ARAP iterations", GH_ParamAccess.item, 1000);
            idx = pManager.AddIntegerParameter("Pinned Vertices", "PV", "The indices of the pinned vertices", GH_ParamAccess.list);
            pManager[idx].Optional = true;
            idx = pManager.AddPointParameter("Pin Positions", "PP", "The coordinates of the pinned vertices", GH_ParamAccess.list);
            pManager[idx].Optional = true;
            pManager.AddBooleanParameter("Force 2D", "2D", "Force the deformed mesh in 2D as in flattening", GH_ParamAccess.item, false);
        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
            pManager.AddMeshParameter("Deformed", "D", "The deformed mesh", GH_ParamAccess.item);
        }

        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            GH_Mesh mesh = null;
            int max_iter = 0;
            var pins = new List<int>();
            var points = new List<Point3d>();
            bool isUnwrap = false;

            if (!DA.GetData(0, ref mesh))
                return;
            DA.GetData(1, ref max_iter);
            DA.GetDataList(2, pins);
            DA.GetDataList(3, points);
            DA.GetData(4, ref isUnwrap);

            ARAPHelper.Configure(max_iter, 0, 0, 0, 0);

            Mesh mesh3D = mesh.Value.DuplicateMesh();
            mesh3D.Faces.ConvertQuadsToTriangles();

            for (int i = 0; i < pins.Count; i++)
            {
                if (pins[i] >= mesh.Value.Vertices.Count)
                {
                    pins.RemoveAt(i);
                    i--;
                }
            }

            if (pins.Count == 0)
            {
                pins.Add(0);
            }

            while (points.Count < pins.Count)
            {
                points.Add(mesh.Value.Vertices[pins[points.Count]]);
            }

            // Check duplicate pins
            var checkSet = new HashSet<int>();
            for (int i = 0; i < pins.Count; ++i)
            {
                if (checkSet.Contains(pins[i]))
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Found duplicated pins, only the first occurence is used.");
                    pins.RemoveAt(i);
                    points.RemoveAt(i);
                    i--;
                    continue;
                }
                checkSet.Add(pins[i]);
            }

            Mesh deformed = mesh3D.DuplicateMesh();

            try
            {
                if (isUnwrap)
                {
                    var positions = new List<Point2d>();
                    for (int i = 0; i < points.Count; i++)
                    {
                        positions.Add(new Point2d(points[i].X, points[i].Y));
                    }
                    ARAPHelper.Unwrap(mesh3D, deformed, pins, positions);
                }
                else
                {
                    ARAPHelper.Deform(mesh3D, deformed, pins, points);
                }
            }
            catch (Exception ex)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, ex.Message);
                return;
            }

            deformed.Faces.Clear();
            deformed.Faces.AddFaces(mesh.Value.Faces);

            deformed.RebuildNormals();
            var ret = new GH_Mesh(deformed);
            DA.SetData(0, ret);
        }

        public override GH_Exposure Exposure => GH_Exposure.tertiary;

        /// <summary>
        /// Provides an Icon for the component.
        /// </summary>
        protected override System.Drawing.Bitmap Icon => Properties.Resources.DeformIcon;

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid => new("cba10d67-bfa1-4ceb-a61c-37c5e11e6fe7");
    }
}
303 files24 directories