using System;
using System.Collections.Generic;
using System.Drawing;
using Grasshopper.Kernel;
using Rhino.Geometry;
namespace CurveSlopeAnalysis
{
public class CurveSlopeAnalysisComponent : GH_Component
{
///
/// Each implementation of GH_Component must provide a public
/// constructor without any arguments.
/// Category represents the Tab in which the component will appear,
/// Subcategory the panel. If you use non-existing tab or panel names,
/// new tabs/panels will automatically be created.
///
public CurveSlopeAnalysisComponent()
: base("CurveSlopeAnalysis", "Nickname",
"Description",
"Category", "Subcategory")
{
}
///
/// Registers all the input parameters for this component.
///
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddGeometryParameter("Curve or Polyline to evaluate", "C", "Curve to evaluate the slope", GH_ParamAccess.item);
pManager.AddNumberParameter("Length to split Curve", "l", "If a Curve is supplied it will be split by segments of l", GH_ParamAccess.item);
pManager.AddNumberParameter("Angle values", "V", "Angle domains for colouring the segments", GH_ParamAccess.list);
pManager.AddColourParameter("Colours to apply to angles", "Col", "Colours to apply to the CurveSegments to visualize slope", GH_ParamAccess.list);
}
///
/// Registers all the output parameters for this component.
///
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
pManager.AddNumberParameter("Angle values", "A", "List of calculated angle values for each segment", GH_ParamAccess.list);
}
protected override void BeforeSolveInstance()
{
if (_cloud != null)
_cloud.Dispose();
_cloud = new PointCloud();
_clippingBox = BoundingBox.Empty;
_anchors = new List();
_labels = new List();
_colours = new List();
}
protected override void SolveInstance(IGH_DataAccess access)
{
// initalize and get geo
Curve curve = null;
double length = double.NaN;
List values = new List();
List palette = new List();
if (!access.GetData(0, ref curve)) return;
if (!access.GetData(1, ref length)) return;
if (!access.GetDataList(2, values)) return;
if (!access.GetDataList(3, palette)) return;
if (values.Count != palette.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "You must supply equal numbers of values and colours.");
while (palette.Count < values.Count)
palette.Add(Color.DeepPink);
}
BoundingBox box = curve.GetBoundingBox(false);
_clippingBox.Union(box);
double[] ts = curve.DivideByLength(length, true);
Curve[] fragments = curve.Split(ts);
List angles = new List(fragments.Length);
foreach (Curve fragment in fragments)
{
Line line = new Line(fragment.PointAtStart, fragment.PointAtEnd);
Vector3d tangent = -line.Direction; // Note, the vector was reversed in the original code, hence the minus sign.
Point3d middle = line.PointAt(0.5);
double angle = Vector3d.VectorAngle(tangent, Plane.WorldXY.ZAxis);
angle = Rhino.RhinoMath.ToDegrees(angle);
angle -= 90;
if (angle < 0)
angle = -angle;
angles.Add(angle);
_anchors.Add(middle);
_labels.Add(string.Format("{0:0.0}°", angle));
// Do you really want to update this on every call to SolveInstance()?
// I commented out this code for now, it complicates things.
// If you do want to do it here, at least move it out of the loop.
//Message = string.Format("{0:0.00}", angles.Min()) + " to " + string.Format("{0:0.00}", angles.Max());
Color colour = Color.DeepPink;
for (int k = 0; k < values.Count; k++)
if (angle < values[k])
{
colour = palette[k];
break;
}
_colours.Add(colour);
_cloud.Add(middle, colour);
}
access.SetDataList(0, angles);
}
#region display
private PointCloud _cloud;
private BoundingBox _clippingBox;
private List _labels;
private List _anchors;
private List _colours;
public override BoundingBox ClippingBox
{
get { return _clippingBox; }
}
public override void DrawViewportWires(IGH_PreviewArgs args)
{
base.DrawViewportWires(args);
if (_cloud != null)
args.Display.DrawPointCloud(_cloud, 3);
Plane plane;
args.Viewport.GetFrustumFarPlane(out plane);
for (int i = 0; i < _labels.Count; i++)
{
double ppu;
args.Viewport.GetWorldToScreenScale(_anchors[i], out ppu);
plane.Origin = _anchors[i];
Rhino.Display.Text3d drawText = new Rhino.Display.Text3d(_labels[i], plane, 20 / ppu);
args.Display.Draw3dText(drawText, _colours[i]);
drawText.Dispose();
}
}
#endregion
///
/// Provides an Icon for every component that will be visible in the User Interface.
/// Icons need to be 24x24 pixels.
///
protected override Bitmap Icon
{
get
{
// You can add image files to your project resources and access them like this:
//return Resources.IconForThisComponent;
return null;
}
}
///
/// Each component must have a unique Guid to identify it.
/// It is vital this Guid doesn't change otherwise old ghx files
/// that use the old ID will partially fail during loading.
///
public override Guid ComponentGuid
{
get { return new Guid("9f09b7c4-5a5d-4ab1-9655-903035367385"); }
}
}
}