using FeMM.Common.Models;
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 DecompensationComponent : GH_Component
    {
        /// <summary>
        /// Initializes a new instance of the CompensationComponent class.
        /// </summary>
        public DecompensationComponent()
          : base("Decompensation", "DC", "Decompensate the flatten patches", CategoryNameConstants.CATEGORY_FEMM, CategoryNameConstants.SUBCATEGORY_PATTERNING)
        {
        }

        /// <summary>
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddCurveParameter("Curves", "C", "The flatten panel to decompensate", GH_ParamAccess.list);
            pManager.AddGenericParameter("Descriptors", "D", "The decompensation descriptors for each curve", GH_ParamAccess.list);
        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
            pManager.AddCurveParameter("Curves", "C", "The decompensated panel curves", GH_ParamAccess.list);
        }

        /// <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)
        {
            var curves = new List<GH_Curve>();
            var objects = new List<object>();
            var descriptors = new List<DecompensationDescriptor>();

            if (!DA.GetDataList(0, curves))
                return;
            if (!DA.GetDataList(1, objects))
                return;

            for (int i = 0; i < objects.Count; i++)
            {
                var descriptor = (objects[i] as GH_Goo<object>).Value as DecompensationDescriptor;
                if (descriptor != null)
                {
                    descriptors.Add(descriptor);
                }
            }

            if (curves.Count != descriptors.Count)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "The number of curves and descriptors must match");
                return;
            }

            var outCurves = new List<PolylineCurve>();

            var center = new Point2d(0, 0);
            for (int i = 0; i < curves.Count; i++)
            {
                center.X = center.X + curves[i].Value.PointAtStart.X;
                center.Y = center.Y + curves[i].Value.PointAtStart.Y;
                center.X = center.X + curves[i].Value.PointAtEnd.X;
                center.Y = center.Y + curves[i].Value.PointAtEnd.Y;
            }

            center.X = center.X / (2 * curves.Count);
            center.Y = center.Y / (2 * curves.Count);

            for (int i = 0; i < curves.Count; i++)
            {
                var curve = new PolylineCurve(curves[i].Value as PolylineCurve);
                var descriptor = descriptors[i];

                var curveLength = curve.GetLength();

                for (int j = 0; j < descriptor.Points.Count; j++)
                {
                    var descriptorPoint = descriptor.Points[j];
                    if (!descriptorPoint.IsNormalized)
                    {
                        descriptorPoint.Length = descriptorPoint.Length / curveLength;
                        descriptorPoint.IsNormalized = true;
                    }

                    if (descriptorPoint.Length < 0)
                    {
                        descriptorPoint.Length = 1 + descriptorPoint.Length;
                    }

                    var param = 0.0;
                    curve.NormalizedLengthParameter(descriptorPoint.Length, out param);
                    var cc = curve.Split(param);
                    if (cc != null && cc.Length > 1)
                    {
                        curve = Curve.JoinCurves(cc)[0] as PolylineCurve;
                    }

                    // TODO: check that the curve is not flipped
                }

                outCurves.Add(curve);
                descriptors[i] = descriptor;
            }

            for (int i = 0; i < outCurves.Count; i++)
            {
                for (int j = 0; j < outCurves[i].PointCount; j++)
                {
                    var nl = GetPointNormalizedLength(outCurves[i], j);

                    var (warp, weft) = GetDecompensation(nl, descriptors[i]);

                    var pt = outCurves[i].Point(j);
                    pt.X = center.X + (pt.X - center.X) * warp;
                    pt.Y = center.Y + (pt.Y - center.Y) * weft;
                    outCurves[i].SetPoint(j, pt);
                }
            }

            DA.SetDataList(0, outCurves);
        }

        private double GetPointNormalizedLength(PolylineCurve curve, int index)
        {
            if (index == 0) { return 0.0; }
            if (index == curve.PointCount - 1) { return 1.0; }
            var param = 0.0;
            var norml = 0.0;
            curve.ClosestPoint(curve.Point(index), out param);
            norml = Math.Min(param / (curve.Domain.Max - curve.Domain.Min), 1.0);
            return norml;
        }

        private (double, double) GetDecompensation(double normalizedLength, DecompensationDescriptor descriptor)
        {
            var pre = 0;
            var post = descriptor.Points.Count - 1;

            for (int i = 0; i < post; i++)
            {
                if (descriptor.Points[i].Length > normalizedLength)
                {
                    break;
                }
                pre = i;
            }

            for (int i = post; i > pre; i--)
            {
                if (descriptor.Points[i].Length < normalizedLength)
                {
                    break;
                }
                post = i;
            }

            var length = descriptor.Points[post].Length - descriptor.Points[pre].Length;
            var multPre = (length - (normalizedLength - descriptor.Points[pre].Length)) / length;
            var multPost = (length - (descriptor.Points[post].Length - normalizedLength)) / length;

            var warp = descriptor.Points[pre].Warp * multPre + descriptor.Points[post].Warp * multPost;
            var weft = descriptor.Points[pre].Weft * multPre + descriptor.Points[post].Weft * multPost;

            return (warp, weft);
        }

        public override GH_Exposure Exposure => GH_Exposure.quarternary;

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

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid => new("a9e861a6-31e7-46ea-b3de-f39ad896bd6a");
    }
}
303 files24 directories