How to create a C# code similar to the Gradient component?

Hello!
I am currently learning about C#, and I am wondering whether some of the components could be replaced by code in C#.
I am working on this script, and I want to write a program that will perform the same function as gradient and return the material. I have already replaced three of the components, yet I am struggling with this one. Can you please help :thinking: ?
The vague idea of what I want to do:

Here is my code:

 List<Line> lines = new List<Line>();
    lines.Add(inputLine);
    DivideAndRotate(inputLine, ref lines, angle, minLength);
    Lines = lines;

    //Substituting the length component
    List<double> lengths = new List<double>();
    foreach (Line itemLine in lines)
    {
      lengths.Add(itemLine.Length);
    }
    Lengths = lengths;

    //substituting the Bounds component
    double minValueBounds = lengths.Min();
    double maxValueBounds = lengths.Max();
    Interval bounds = new Interval(minValueBounds, maxValueBounds);
    Domain = bounds;

    //substituting the remap component
    List<double> remaps = new List<double>();
    foreach(double length in lengths )
    {
      remaps.Add(RemapValues(length, minValueBounds, maxValueBounds));
    }
    Remap = remaps;

    //I am not really sure if I should use one of these methods, and which one?
    // How should I do the Gradient function?


    //Rhino.Display.DisplayMaterial material = new Rhino.Display.DisplayMaterial();
    //material = material.Specular(System.Drawing.Color.Lime);

    Rhino.DocObjects.Material material = new Rhino.DocObjects.Material();
    //material.EmissionColor(System.Drawing.Color.Lime);
    material = Rhino.DocObjects.Material.DefaultMaterial;
    material.SpecularColor = System.Drawing.Color.Lime;
    //material.Name = "MyMaterial";

    Material = material;

The GH file:
Gradient_Material_Question.gh (18.3 KB)

Do you want it to have the same interface as the gradient component or just output a gradient of colors by inputting colors?

@Michael_Pryor
Thanks, for your quick reply.
Answering your question, I do not want to make a new C# component that performs the same function, I just want to add into my already existing code.

My input (in the code is named remaps) would be the remapped numbers, this I already programmed into C#, now I just want to colour them differently for each value, therefore, I need to translate the values into colours, if it is possible using the same presets as the Gradient component has.

You can find Grasshoppers gradient presets here in the GH_Gradient class:
https://developer.rhino3d.com/wip/api/grasshopper/html/Methods_T_Grasshopper_GUI_Gradient_GH_Gradient.htm

And you can get a color in the gradient by parameter (t) value with the GH_Gradient.ColourAt method https://developer.rhino3d.com/wip/api/grasshopper/html/M_Grasshopper_GUI_Gradient_GH_Gradient_ColourAt.htm

2 Likes

You can override DrawViewportWires method to draw your lines with custom colors directly:

private void RunScript(Line inputLine, double angle, double minLength, ref object Lines, ref object Lengths, ref object Domain, ref object Remap)
{
  var lines = new List<Line>(){inputLine};
  DivideAndRotate(inputLine, lines, angle, minLength);
  Lines = lines;
  //Substituting the length component
  var lengths = lines.Select(line => line.Length).ToList();
  Lengths = lengths;
  //substituting the Bounds component
  var bounds = new Interval(lengths.Min(), lengths.Max());
  Domain = bounds;
  //substituting the remap component
  var remap = lengths.Select(len => RemapValues(len, bounds.Min, bounds.Max)).ToList();
  Remap = remap;
  _lines = lines;
  _parameters = remap;
}

// <Custom additional code> 
private static void DivideAndRotate(Line line, List<Line> lines, double angle, double minLength)
{
  if(line.Length < minLength) return;
  var endpt = line.PointAt(0.95);
  line.To = endpt;
  var xform = Transform.Rotation(angle, Vector3d.ZAxis, line.From);
  line.Transform(xform);
  lines.Add(line);
  DivideAndRotate(line, lines, angle, minLength);
}
private static double RemapValues (double aValue, double leftMin, double leftMax)
{
  var leftSpan = leftMax - leftMin;
  return (aValue - leftMin) / leftSpan;
}
//Draw lines with custom colors
List<double> _parameters;
List<Line> _lines;
public override void DrawViewportWires(IGH_PreviewArgs args)
{
  var gradiant = Grasshopper.GUI.Gradient.GH_Gradient.Heat();
  if (_lines == null) return;
  for (var i = 0; i < _lines.Count; i++)
    args.Display.DrawLine(_lines[i], gradiant.ColourAt(_parameters[i]), 2);
}
// </Custom additional code> 

Gradiant.gh (15.7 KB)

3 Likes

@Mahdiyar
Hi!
Thank you very much for the time you spent working on my problem, I saw that you put in a lot of effort into it,I appreciate this very much! When I saw your code, I couldn’t believe how organized it was, thank you for improving my remaining code.
I have a few questions regarding these lines:

I am not quite sure why these lines are necessary for the code, as we already defined that _parameters = remaps, and _lines = lines. So why should we now write them now above the function?

Here I don’t quite understand what this line of code is doing. I think it is displaying the colours on the geometry, but I am not quite sure…

P.S. Thanks for showing me Linq, I didn’t know that you could shorten the code so much with it!!!

If you consider the script being one class, you do have multiple potential members:

methods
constructors
fields
properties
events/delegates
(sub)classes/-structs

You are probably just used to write code in methods, meaning you create local variables and modify them. But as soon as you declare them within a method, they only live within this scope.
As soon as you need to pass variables across other methods, you get a problem. One option is to pass them as argument to another function. But this is not very practical, because sometimes you don‘t have access to modify the signature of another method or you don‘t want to pass dozens of arguments or you don‘t want to call this method directly… So you can declare this as a field or property instead and call them from any instance method (=non static).

private int _somePrivateField = 42;
public int SomeProperty {get{return _somePrivateField;}}

public void DoSomething()
{ _somePrivateField += 24;}

1 Like