using FeMM.Common.Models;
using FeMM.Grasshopper.DataTypes.FeMM;
using FeMM.Grasshopper.Helpers;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino;
using Rhino.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using TensileLib.FormFinding;
using TensileLib.Geometry;
namespace FeMM.Grasshopper.Components.Patterning
{
#if NETCOREAPP
[SupportedOSPlatform("windows")]
#endif
public class CompensationComponent_OBSOLETE : GH_Component
{
/// <summary>
/// Initializes a new instance of the CompensationComponent class.
/// </summary>
public CompensationComponent_OBSOLETE()
: base("Compensation", "C", "Compensate 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.AddMeshParameter("Panel", "P", "The flatten panel to compensate", GH_ParamAccess.item);
pManager.AddVectorParameter("Warp direction", "W", "The vector of the warp firection of the panel", GH_ParamAccess.item);
pManager.AddNumberParameter("Wa Scale (%)", "Wa%", "The deformation in the warp direction in %(greater than 0 = reduction)", GH_ParamAccess.item);
pManager.AddNumberParameter("We Scale (%)", "We%", "The deformation in the weft direction in %(greater than 0 = reduction)", GH_ParamAccess.item);
pManager.AddGenericParameter("Fixed lengths", "FLs", "The fixed lengths of edges, for example borders, defined with the Fixed compensation component", GH_ParamAccess.list);
pManager[pManager.ParamCount - 1].Optional = true;
pManager.AddIntegerParameter("Max Iter NL", "MI,NL", "Max number of iterations in non linear solution (used in non linear solver or updating pressures direction)", GH_ParamAccess.item, 20);//reduced at default
pManager.AddNumberParameter("Tolerance NL", "T NL", "Tolerance on relative error durign non linear solution (used in non linear solver or updating pressures direction)", GH_ParamAccess.item, 0.0001);
pManager.AddIntegerParameter("Max Iter,s", "MI,s", "Max number of iterations for the solver", GH_ParamAccess.item, 10000);
pManager.AddNumberParameter("Tolerance,s", "T,s", "Tolerance for the solver", GH_ParamAccess.item, 0.0001);
//pManager.AddNumberParameter("Top Scale (%)", "T", "The scale factor in the relative X axis of the top side", GH_ParamAccess.item);
//pManager.AddNumberParameter("Bottom Scale (%)", "B", "The scale factor in the relative X axis of the top side", GH_ParamAccess.item);
//pManager.AddNumberParameter("Transaction Length", "L", "The transaction length beetweel the vertical and horizontal sides", GH_ParamAccess.item);
//pManager.AddIntegerParameter("Junction type", "J", "Set the junction type between the top, bottom, left and right side", GH_ParamAccess.item, 0);
//Param_Integer jParam = pManager[7] as Param_Integer;
//jParam.AddNamedValue("Arc", 0);
//jParam.AddNamedValue("Line", 1);
}
/// <summary>
/// Registers all the output parameters for this component.
/// </summary>
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddMeshParameter("Panel", "P", "The compensated panel", 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)
{
var panel = new GH_Mesh();
Vector3d warp_dir = Vector3d.Unset;
double waPerc = 0;
double wePerc = 0;
int maxIterNL = 0;
double tol = 0;
int maxIterSolver = 0;
double epsS = 0;
var fixedBeams = new List<GH_Beam>();
if (!DA.GetData(0, ref panel))
return;
if (!DA.GetData(1, ref warp_dir))
return;
if (!DA.GetData(2, ref waPerc))
return;
if (!DA.GetData(3, ref wePerc))
return;
DA.GetDataList(4, fixedBeams);
if (!DA.GetData(5, ref maxIterNL))
return;
if (!DA.GetData(6, ref tol))
return;
if (!DA.GetData(7, ref maxIterSolver))
return;
if (!DA.GetData(8, ref epsS))
return;
//This component use the flattening solver of tensile lib. The idea is to impose fixed lengths as compensated to the panel.
panel.Value.Faces.ConvertQuadsToTriangles();
if (Math.Abs(warp_dir.Z) > RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)
{
throw new NotSupportedException("Warp dir must be defined on plane xy");
}
//Testing planar mesh
for (int i = 0; i < panel.Value.Vertices.Count; i++)
{
if (Math.Abs(panel.Value.Normals[i] * Vector3d.ZAxis) < 0.999)
{
throw new NotSupportedException("Face " + (i + 1).ToString() + " not on plane xy");
}
}
//x warp
//y weft
var dirX = new Vector2d(warp_dir.X, warp_dir.Y);
dirX.Unitize();
var dirY = new Vector2d(-dirX.Y, dirX.X);
// Create the model for the flattening solver starting from the mesh.
var model = new Model();
var modifiedNodes = new Dictionary<int, Point3d>();
int index = 0;
for (int i = 0; i < panel.Value.Vertices.Count; i++)
{
Point3d pt = (Point3d)panel.Value.Vertices.ElementAt(i);
//original nodes
model.AddNewNode(pt.X, pt.Y, pt.Z);
//Modified position due to normal stretch
double localX = pt.X * dirX.X + pt.Y * dirX.Y;
double localY = pt.X * dirY.X + pt.Y * dirY.Y;
//stretching of warp and weft deformations (>0=reduction)
localX *= 1 - waPerc / 100;
localY *= 1 - wePerc / 100;
modifiedNodes.Add(index, new Point3d(localX, localY, pt.Z));
index++;
}
for (int i = 0; i < panel.Value.Faces.Count; i++)
{
MeshFace face = panel.Value.Faces.ElementAt(i);
if (!face.IsTriangle)
{
throw new NotSupportedException();
}
for (int edgeId = 0; edgeId < 3; edgeId++)
{
double fixedLength = double.MinValue;
double weigth = double.MinValue;
int firstIndex;
int secondIndex;
//A=0;B=1;C=2
if (edgeId == 0)
{
firstIndex = face.A;
secondIndex = face.B;
}
else if (edgeId == 1)
{
firstIndex = face.B;
secondIndex = face.C;
}
else
{
firstIndex = face.C;
secondIndex = face.A;
}
Node node1 = model.Nodes.ElementAt(firstIndex);
Node node2 = model.Nodes.ElementAt(secondIndex);
Beam existing = model.Beams.FirstOrDefault(b => b.Node1.Id == node1.Id && b.Node2.Id == node2.Id || b.Node1.Id == node2.Id && b.Node2.Id == node1.Id);
if (existing != null)
continue;
Point3d firstPOriginal = panel.Value.Vertices[firstIndex];
Point3d secondPOriginal = panel.Value.Vertices[secondIndex];
Point3d firstPMod = modifiedNodes[firstIndex];
Point3d secondPMod = modifiedNodes[secondIndex];
//serching fixed beam
for (int j = 0; j < fixedBeams.Count; j++)
{
GH_Beam beam = fixedBeams[j];
if (beam.Value.PointFrom.DistanceTo(firstPOriginal) < RhinoDoc.ActiveDoc.ModelAbsoluteTolerance &&
beam.Value.PointTo.DistanceTo(secondPOriginal) < RhinoDoc.ActiveDoc.ModelAbsoluteTolerance ||
beam.Value.PointTo.DistanceTo(firstPOriginal) < RhinoDoc.ActiveDoc.ModelAbsoluteTolerance &&
beam.Value.PointFrom.DistanceTo(secondPOriginal) < RhinoDoc.ActiveDoc.ModelAbsoluteTolerance)
{
//found
//searching if beam have fixed length
for (int k = 0; k < beam.Value.Loads.Count; k++)
{
LoadModel lm = beam.Value.Loads[k];
FixedLengthModel flm = lm as FixedLengthModel;
if (flm != null)
{
fixedLength = flm.Value;
weigth = flm.Weight;
break;
}
}
if (fixedLength > 0)
break;
}
}
if (fixedLength < 0)
{
fixedLength = firstPMod.DistanceTo(secondPMod);
weigth = 1;
}
int beamId = model.AddNewBeam(node1.Id, node2.Id);
model.Beams[beamId].FixNLLength(fixedLength, weigth);
}
model.AddNewPlate(model.Nodes.ElementAt(face.A).Id, model.Nodes.ElementAt(face.B).Id, model.Nodes.ElementAt(face.C).Id);
}
try
{
// Solve
var solver = new PlanarFixedLengthsSolver(model, maxIterNL, tol, epsS, maxIterSolver);
Solver.ResultCodes ret = solver.Solve(out PlanarFixedLengthsSolver.PlanarFixedLengthsResult planarFixedLengthsResult);
switch (ret)
{
case Solver.ResultCodes.Converged:
case Solver.ResultCodes.ConvergedUsingSlowMethodWithoutWeigths:
{
break;
}
case Solver.ResultCodes.NotconvergedAtMaxIterationNumber:
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Not converged");
break;
}
default:
throw new NotSupportedException();
}
}
catch (Exception)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Fail to solve");
}
// If success make a new compensated mesh
var compensated = new Mesh();
for (int i = 0; i < model.Nodes.Count; i++)
{
Node node = model.Nodes.ElementAt(i);
compensated.Vertices.Add(node.X, node.Y, node.Z);
}
for (int i = 0; i < model.Plates.Count; i++)
{
Plate plate = model.Plates.ElementAt(i);
int i1 = model.Nodes.IndexOf(plate.Node1);
int i2 = model.Nodes.IndexOf(plate.Node2);
int i3 = model.Nodes.IndexOf(plate.Node3);
int i4 = model.Nodes.IndexOf(plate.Node4);
if (plate.PlateType == Plate.PlateTypes.Quad4)
compensated.Faces.AddFace(i1, i2, i3, i4);
else if (plate.PlateType == Plate.PlateTypes.Tri3)
compensated.Faces.AddFace(i1, i2, i3);
}
compensated.Normals.ComputeNormals();
compensated.Compact();
DA.SetData(0, compensated);
}
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("c64f8be6-7169-46b2-a501-8a6fab04c724");
}
}