using FeMM.Common.Helpers;
using FeMM.Grasshopper.Helpers;
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using Rhino.Geometry.Intersect;
using System;
using System.Collections.Generic;
using System.Linq;
namespace FeMM.Grasshopper.Components.Patterning
{
public class CompoundNetComponent : GH_Component
{
/// <summary>
/// Initializes a new instance of the BeamComponent class.
/// </summary>
public CompoundNetComponent()
: base("Compound Net", "CompNet", "Create a compound cable net", CategoryNameConstants.CATEGORY_FEMM, CategoryNameConstants.SUBCATEGORY_PATTERNING)
{
}
/// <summary>
/// Registers all the input parameters for this component.
/// </summary>
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int idR = pManager.AddCurveParameter("Restraint edges", "R", "The restraint boundary curve", GH_ParamAccess.tree);
pManager[idR].Optional = true;
int idF = pManager.AddCurveParameter("Free edges", "F", "The free boundary curve", GH_ParamAccess.tree);
pManager[idF].Optional = true;
int idP = pManager.AddCurveParameter("Perimeter", "PE", "The perimeter of the grid", GH_ParamAccess.tree);
pManager[idP].Optional = true;
pManager.AddNumberParameter("Warp step", "Warp", "The warp distance step", GH_ParamAccess.list);
pManager.AddNumberParameter("Weft step", "Weft", "The weft distance step", GH_ParamAccess.list);
int idK = pManager.AddNumberParameter("Kind", "K", "Kind of the net: 0 = Grid; 1 = Radial;", GH_ParamAccess.list, 0);
pManager[idK].Optional = true;
int idO = pManager.AddPointParameter("Origin", "O", "The origin point of the cable net", GH_ParamAccess.list, Point3d.Origin);
pManager[idO].Optional = true;
int idA = pManager.AddNumberParameter("Angle", "A", "The angle of rotation of the cable net", GH_ParamAccess.list, 0);
pManager[idA].Optional = true;
int idT = pManager.AddNumberParameter("Tolerance", "T", "The intersection tolerance", GH_ParamAccess.item, 0.001);
pManager[idT].Optional = true;
int idM = pManager.AddNumberParameter("Match", "M", "The matching tolerance", GH_ParamAccess.item, 0);
pManager[idM].Optional = true;
int idD = pManager.AddNumberParameter("Discretization", "D", "The discretization of the surface (higher means slower)", GH_ParamAccess.item, 25);
pManager[idD].Optional = true;
}
/// <summary>
/// Registers all the output parameters for this component.
/// </summary>
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddCurveParameter("Restraint edges", "RB", "The border lines", GH_ParamAccess.tree);
pManager.AddCurveParameter("Free edges", "FB", "The border lines", GH_ParamAccess.tree);
pManager.AddCurveParameter("Warp", "Warp", "The warp lines", GH_ParamAccess.list);
pManager.AddCurveParameter("Weft", "Weft", "The weft lines", GH_ParamAccess.list);
pManager.AddPointParameter("Restraint points", "RP", "The boundary points", GH_ParamAccess.list);
pManager.AddPointParameter("NotRestraint points", "NRP", "The boundary points", GH_ParamAccess.list);
pManager.AddPointParameter("Internal points", "IP", "The internal points", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess DA)
{
GH_Structure<GH_Curve> fixCurveInputs;
GH_Structure<GH_Curve> freeCurveInputs;
GH_Structure<GH_Curve> perimeterInputs;
var warpSteps = new List<double>();
var weftSteps = new List<double>();
var netKinds = new List<double>(); // If int it does not take inputs
var origins = new List<Point3d>();
var angles = new List<double>();
double tolerance = 0.1;
double matching = 0;
double discretization = 25.0;
DA.GetDataTree("Restraint edges", out fixCurveInputs);
DA.GetDataTree("Free edges", out freeCurveInputs);
DA.GetDataTree("Perimeter", out perimeterInputs);
if (!DA.GetDataList("Warp step", warpSteps))
return;
if (!DA.GetDataList("Weft step", weftSteps))
return;
DA.GetDataList("Kind", netKinds);
DA.GetDataList("Origin", origins);
DA.GetDataList("Angle", angles);
DA.GetData("Tolerance", ref tolerance);
DA.GetData("Match", ref matching);
DA.GetData("Discretization", ref discretization);
if (freeCurveInputs.DataCount == 0 && fixCurveInputs.DataCount == 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input curves cannot be empty");
return;
}
if (warpSteps.Count != weftSteps.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Warp and weft steps lists must have the same length");
return;
}
for (int i = 0; i < warpSteps.Count; i++)
{
if (warpSteps[i] == 0 && weftSteps[i] == 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Warp or weft step can not be 0");
return;
}
}
var netNumber = 0;
for (int i = 0; i < perimeterInputs.PathCount; ++i)
{
var path = perimeterInputs.Paths[i];
netNumber = Math.Max(path.Indices[0] + 1, netNumber);
}
while (warpSteps.Count < netNumber)
{
warpSteps.Add(warpSteps[warpSteps.Count - 1]);
}
while (weftSteps.Count < netNumber)
{
weftSteps.Add(weftSteps[weftSteps.Count - 1]);
}
while (netKinds.Count < netNumber)
{
netKinds.Add(0);
}
while (origins.Count < netNumber)
{
origins.Add(new Point3d(0, 0, 0));
}
while (angles.Count < netNumber)
{
angles.Add(0);
}
var inFixCables = new List<List<Curve>>();
var inFreeCables = new List<List<Curve>>();
var inPerimeters = new List<List<Curve>>();
var uniqueFixSet = new HashSet<Guid>();
var uniqueFreeSet = new HashSet<Guid>();
var uniqueFix = new List<Curve>();
var uniqueFree = new List<Curve>();
for (int i = 0; i < netNumber; i++)
{
var ghfixcurves = fixCurveInputs.Paths.Contains(new GH_Path(i)) ? (List<GH_Curve>)fixCurveInputs.get_Branch(i) : [];
var ghfreecurves = freeCurveInputs.Paths.Contains(new GH_Path(i)) ? (List<GH_Curve>)freeCurveInputs.get_Branch(i) : [];
var ghperimeters = perimeterInputs.Paths.Contains(new GH_Path(i)) ? (List<GH_Curve>)perimeterInputs.get_Branch(i) : [];
inFixCables.Add([]);
inFreeCables.Add([]);
inPerimeters.Add([]);
for (int j = 0; j < ghfixcurves.Count; j++)
{
inFixCables[i].Add(ghfixcurves[j].Value);
if (!uniqueFixSet.Contains(ghfixcurves[j].ReferenceID))
{
uniqueFixSet.Add(ghfixcurves[j].ReferenceID);
uniqueFix.Add(ghfixcurves[j].Value);
}
}
for (int j = 0; j < ghfreecurves.Count; j++)
{
inFreeCables[i].Add(ghfreecurves[j].Value);
if (!uniqueFreeSet.Contains(ghfreecurves[j].ReferenceID))
{
uniqueFreeSet.Add(ghfreecurves[j].ReferenceID);
uniqueFree.Add(ghfreecurves[j].Value);
}
}
for (int j = 0; j < ghperimeters.Count; j++)
{
inPerimeters[i].Add(ghperimeters[j].Value);
}
}
var allOutWarp = new List<List<LineCurve>>();
var allOutWeft = new List<List<LineCurve>>();
var allOutFreeBorder = new List<List<Curve>>();
var allOutFixBorder = new List<List<Curve>>();
var allRestrain = new List<Point3d>();
var allNotRestrain = new List<Point3d>();
var allInternalPoint = new List<Point3d>();
// TODO: Check for nulls
//if (freeCurveInput.Any(i => i == null))
//{
// AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Null items in free edges list");
// return;
//}
//if (fixCurveInput.Any(i => i == null))
//{
// AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Null items in restraint edges list");
// return;
//}
for (int i = 0; i < netNumber; ++i)
{
var outWarp = new List<List<LineCurve>>();
var outWeft = new List<List<LineCurve>>();
var outFreeBorder = new List<List<Curve>>();
var outFixBorder = new List<List<Curve>>();
var restrain = new List<Point3d>();
var notRestrain = new List<Point3d>();
var internalPoint = new List<Point3d>();
var surfacePoints = new List<Point3d>();
for (int j = 0; j < inFixCables[i].Count; ++j)
{
foreach (var param in inFixCables[i][j].DivideByCount((int)discretization, true))
{
surfacePoints.Add(inFixCables[i][j].PointAt(param));
}
}
for (int j = 0; j < inFreeCables[i].Count; ++j)
{
foreach (var param in inFreeCables[i][j].DivideByCount((int)discretization, false))
{
surfacePoints.Add(inFreeCables[i][j].PointAt(param));
}
}
Plane plane;
Plane.FitPlaneToPoints(surfacePoints, out plane);
// MAKE THE NET
try
{
var refPlane = CableNetHelper.MakePlane(plane, origins[i], angles[i] / 180 * Math.PI);
if (netKinds[i] == 0)
{
if (!CableNetHelper.CreateCableNet2(inFixCables[i], inFreeCables[i], inPerimeters[i], warpSteps[i], weftSteps[i], refPlane,
out outWarp, out outWeft, out outFreeBorder, out outFixBorder, out restrain, out notRestrain, out internalPoint, tolerance, matching))
return;
}
else if (netKinds[i] == 1)
{
if (!CableNetHelper.CreateRadialCableNet2(inFixCables[i], inFreeCables[i], inPerimeters[i], (int)warpSteps[i], weftSteps[i], refPlane,
out outWarp, out outWeft, out outFreeBorder, out outFixBorder, out restrain, out notRestrain, out internalPoint, tolerance, matching))
return;
}
}
catch (Exception e)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Fail to create the cable net: " + e.Message);
}
// PROJECT TO PATCH
var patch = Brep.CreatePatch(new List<GeometryBase>() { new PointCloud(surfacePoints) }, (int)discretization, (int)discretization, tolerance);
var projectedPoints = Intersection.ProjectPointsToBreps(new List<Brep>() { patch }, internalPoint, plane.Normal, tolerance).ToList();
var dict = new Dictionary<Point3d, int>();
for (int k = 0; k < internalPoint.Count; k++)
{
dict.Add(internalPoint[k], k);
}
// ADJUST WARP POITS WITH PROJECTION
for (int k = 0; k < outWarp.Count; k++)
{
for (int n = 0; n < outWarp[k].Count; n++)
{
if (dict.ContainsKey(outWarp[k][n].PointAtStart))
{
outWarp[k][n].SetStartPoint(projectedPoints[dict[outWarp[k][n].PointAtStart]]);
}
if (dict.ContainsKey(outWarp[k][n].PointAtEnd))
{
outWarp[k][n].SetEndPoint(projectedPoints[dict[outWarp[k][n].PointAtEnd]]);
}
}
}
// ADJUST WEFT POITS WITH PROJECTION
for (int k = 0; k < outWeft.Count; k++)
{
for (int n = 0; n < outWeft[k].Count; n++)
{
if (dict.ContainsKey(outWeft[k][n].PointAtStart))
{
outWeft[k][n].SetStartPoint(projectedPoints[dict[outWeft[k][n].PointAtStart]]);
}
if (dict.ContainsKey(outWeft[k][n].PointAtEnd))
{
outWeft[k][n].SetEndPoint(projectedPoints[dict[outWeft[k][n].PointAtEnd]]);
}
}
}
// ADD TO THE WHOLE LISTS
internalPoint = projectedPoints;
allOutWarp.AddRange(outWarp);
allOutWeft.AddRange(outWeft);
allOutFreeBorder.AddRange(outFreeBorder);
allOutFixBorder.AddRange(outFixBorder);
allRestrain.AddRange(restrain);
allNotRestrain.AddRange(notRestrain);
allInternalPoint.AddRange(internalPoint);
}
// MATCH THE CABLES OF DIFFERENT NETS
var selfFixPoints = new HashSet<Point3d>();
var selfFreePoints = new HashSet<Point3d>();
for (int k = 0; k < allOutFixBorder.Count; k++)
{
for (int n = 0; n < allOutFixBorder[k].Count; n++)
{
selfFixPoints.Add(allOutFixBorder[k][n].PointAtStart);
selfFixPoints.Add(allOutFixBorder[k][n].PointAtEnd);
}
}
for (int k = 0; k < allOutFreeBorder.Count; k++)
{
for (int n = 0; n < allOutFreeBorder[k].Count; n++)
{
selfFreePoints.Add(allOutFreeBorder[k][n].PointAtStart);
selfFreePoints.Add(allOutFreeBorder[k][n].PointAtEnd);
}
}
var collapsedRestrain = CollapsePoints(allRestrain, [.. selfFixPoints], matching);
var collapsedNotRestrain = CollapsePoints(allNotRestrain, [.. selfFreePoints], matching);
// GET THE UNIQUES POINTS ON CABLES
var uniqueRestrainSet = new HashSet<Point3d>();
var uniqueNotRestrainSet = new HashSet<Point3d>();
foreach (var entry in collapsedRestrain)
{
if (uniqueRestrainSet.Contains(entry.Value)) continue;
uniqueRestrainSet.Add(entry.Value);
}
foreach (var entry in collapsedNotRestrain)
{
if (uniqueNotRestrainSet.Contains(entry.Value)) continue;
uniqueNotRestrainSet.Add(entry.Value);
}
allRestrain = [.. uniqueRestrainSet];
allNotRestrain = [.. uniqueNotRestrainSet];
allOutFixBorder.Clear();
allOutFreeBorder.Clear();
// SPLIT THE CABLES AT UNIQUE POINTS
for (int i = 0; i < uniqueFix.Count; i++)
{
var splits = new List<double>();
double t = 0;
for (int j = 0; j < allRestrain.Count; j++)
{
if (uniqueFix[i].ClosestPoint(allRestrain[j], out t, matching))
{
splits.Add(t);
}
}
var result = uniqueFix[i].Split(splits).ToList();
if (result.Count > 0)
{
allOutFixBorder.Add(result);
}
else
{
allOutFixBorder.Add([uniqueFix[i]]);
}
}
for (int i = 0; i < uniqueFree.Count; i++)
{
var splits = new List<double>();
double t = 0;
for (int j = 0; j < allNotRestrain.Count; j++)
{
if (uniqueFree[i].ClosestPoint(allNotRestrain[j], out t, matching))
{
splits.Add(t);
}
}
var result = uniqueFree[i].Split(splits).ToList();
if (result.Count > 0)
{
allOutFreeBorder.Add(result);
}
else
{
allOutFreeBorder.Add([uniqueFree[i]]);
}
}
// UPDATE WARP AND WEFT POINTS IN THE SAME POSITION OF THE SPLITS
for (int i = 0; i < allOutWarp.Count; i++)
{
for (int j = 0; j < allOutWarp[i].Count; j++)
{
if (collapsedRestrain.ContainsKey(allOutWarp[i][j].PointAtStart))
{
allOutWarp[i][j].SetStartPoint(collapsedRestrain[allOutWarp[i][j].PointAtStart]);
}
if (collapsedRestrain.ContainsKey(allOutWarp[i][j].PointAtEnd))
{
allOutWarp[i][j].SetEndPoint(collapsedRestrain[allOutWarp[i][j].PointAtEnd]);
}
if (collapsedNotRestrain.ContainsKey(allOutWarp[i][j].PointAtStart))
{
allOutWarp[i][j].SetStartPoint(collapsedNotRestrain[allOutWarp[i][j].PointAtStart]);
}
if (collapsedNotRestrain.ContainsKey(allOutWarp[i][j].PointAtEnd))
{
allOutWarp[i][j].SetEndPoint(collapsedNotRestrain[allOutWarp[i][j].PointAtEnd]);
}
}
}
for (int i = 0; i < allOutWeft.Count; i++)
{
for (int j = 0; j < allOutWeft[i].Count; j++)
{
if (collapsedRestrain.ContainsKey(allOutWeft[i][j].PointAtStart))
{
allOutWeft[i][j].SetStartPoint(collapsedRestrain[allOutWeft[i][j].PointAtStart]);
}
if (collapsedRestrain.ContainsKey(allOutWeft[i][j].PointAtEnd))
{
allOutWeft[i][j].SetEndPoint(collapsedRestrain[allOutWeft[i][j].PointAtEnd]);
}
if (collapsedNotRestrain.ContainsKey(allOutWeft[i][j].PointAtStart))
{
allOutWeft[i][j].SetStartPoint(collapsedNotRestrain[allOutWeft[i][j].PointAtStart]);
}
if (collapsedNotRestrain.ContainsKey(allOutWeft[i][j].PointAtEnd))
{
allOutWeft[i][j].SetEndPoint(collapsedNotRestrain[allOutWeft[i][j].PointAtEnd]);
}
}
}
// OUTPUT RESULTS
int outCount = 0;
DA.SetDataTree(outCount++, ListListToTree(allOutFixBorder));
DA.SetDataTree(outCount++, ListListToTree(allOutFreeBorder));
DA.SetDataTree(outCount++, ListListToTree(allOutWarp));
DA.SetDataTree(outCount++, ListListToTree(allOutWeft));
DA.SetDataList(outCount++, allRestrain);
DA.SetDataList(outCount++, allNotRestrain);
DA.SetDataList(outCount++, allInternalPoint);
}
private Dictionary<Point3d, Point3d> CollapsePoints(List<Point3d> points, List<Point3d> notablePoints, double matching)
{
var notableTree = new RTree();
for (int i = 0; i < notablePoints.Count; ++i)
{
notableTree.Insert(notablePoints[i], i);
}
var map = new Dictionary<Point3d, Point3d>();
var searchTree = new RTree();
for (var i = 0; i < points.Count; i++)
{
if (map.ContainsKey(points[i])) continue;
map.Add(points[i], points[i]);
int notableId = -1;
notableTree.Search(new Sphere(points[i], matching), new EventHandler<RTreeEventArgs>((sender, e) =>
{
if (notableId == -1 || notablePoints[notableId].DistanceTo(points[i]) > notablePoints[e.Id].DistanceTo(points[i]))
{
notableId = e.Id;
}
}));
if (notableId > 0)
{
map[points[i]] = notablePoints[notableId];
continue;
}
var neighbours = new HashSet<int>();
searchTree.Search(new Sphere(points[i], matching), new EventHandler<RTreeEventArgs>((sender, e) => { neighbours.Add(e.Id); }));
foreach (var n in neighbours)
{
map[points[i]] += map[points[n]];
}
map[points[i]] /= neighbours.Count + 1;
foreach (var n in neighbours)
{
searchTree.Remove(map[points[n]], n);
map[points[n]] = map[points[i]];
searchTree.Insert(map[points[n]], n);
}
searchTree.Insert(map[points[i]], i);
}
return map;
}
public override GH_Exposure Exposure => GH_Exposure.primary;
/// <summary>
/// Provides an Icon for the component.
/// </summary>
protected override System.Drawing.Bitmap Icon => Properties.Resources.CompounCableNetComponentIcon;
/// <summary>
/// Gets the unique ID for this component. Do not change this ID after release.
/// </summary>
public override Guid ComponentGuid => new("c5e1549c-7c80-4bb0-a067-117eb2d19240");
private IGH_DataTree ListListToTree(List<List<Curve>> curves)
{
var tree = new DataTree<Curve>();
for (int i = 0; i < curves.Count; i++)
{
tree.AddRange(curves[i], new GH_Path(i));
}
return tree;
}
private IGH_DataTree ListListToTree(List<List<LineCurve>> curves)
{
var tree = new DataTree<LineCurve>();
for (int i = 0; i < curves.Count; i++)
{
tree.AddRange(curves[i], new GH_Path(i));
}
return tree;
}
}
}