C# component is rounding doubles

unhandled

#1

I’m trying to produce a simple bivariate histogram in the C# script component. All is working except that the doubles in histogram list are getting rounded, which off course means that the list only contains 0.0.
What I’m I missing?

I’m working on RhinoMac so the inputs don’t show on the script, they are:
origem is a Point3d (the origin of the histogram)
pts is a List of Points
celX is an integer (the number of cells in X direction)
celY is an integer (the number of cells in Y direction)
celW is a double (the cell width)
celH is a double (the cell height)

    var listR = new List<Rectangle3d>();
    var listVal = new List<double>();
    var unitZ = new Vector3d(0,0,1);
    double h = 0;
    var listHist = new List<double>();

    for(int i = 0; i < celX; i++)
    	{
    	for (int j = 0; j < celY; j++)
    		{
    		var tempPt = new Point3d(origem.X + i*celW, origem.Y + j*celH, 0);
    		var pl = new Plane(tempPt, unitZ);
    		var tempR = new Rectangle3d(pl, celW, celH);
    		int nPt = 0;
    		foreach (Point3d pt in pts)
    			{
    			if (pt.X >= (origem.X + i*celW) && pt.X < (origem.X + (i + 1)*celW) &&
    				pt.Y >= (origem.Y + j*celH) && pt.Y < (origem.Y + (j + 1)*celH))
    				{
    				nPt++;
    				}
    			}
    		h = (1/ pts.Count) * (nPt/tempR.Area);
    		listHist.Add(h);
    		listVal.Add(nPt);
    		listR.Add(tempR);
    		}
    	}

    A = listR;
    B = listVal;
    C = listHist;

(David Rutten) #2

Which doubles are you talking about specifically? It’s all standard C#, so any rounding must specifically happen in code. I think it’s best if you upload a gh file that actually replicates the problem, it’s really difficult debugging code by just looking at it.

ps. one common way in which inadvertent rounding happens in Grasshopper is when people run values (points, doubles, floats, decimals) through a text panel. Text panels convert any data to strings and display those. The output of a panel is those strings so if you convert them back into points or doubles afterwards you’ll get rounding to --usually-- 6 decimal places.


#3

I’m talking about the h values which are added to listHist and output to C. I only get 0.0 in that list.
I’m passing the values to a text panel but the values are less the 4 decimal places. But even if I multiply by 10000 them before outputting them to a text panel I still get 0.

Sorry for not adding the file. Here it is:
Histograma.gh (22.0 KB)


#4

I forgot to internalize the points.

Histograma.gh (17.4 KB)


(David Rutten) #5

Whoa… turns out newlines in script code is not handled well from Mac to Windows. I can probably fix it just by inserting new lines manually though.


(David Rutten) #6

This is your problem I think:

(1 / pts.Count)

Those are both integers, so the answer is always zero, except when the count is 1. You’ll want:

(1.0 / pts.Count)


(David Rutten) #7

I have to say I don’t quite follow the maths behind the histogram value, but it seems to do something now.
Here’s how I would have written the code:

private void RunScript(Point3d origem, List<Point3d> pts, int Nx, int Ny, double W, double H, ref object A, ref object B, ref object C, ref object D)
{
  if (Nx <= 0) throw new ArgumentException("Nx must be a strictly positive value");
  if (Ny <= 0) throw new ArgumentException("Ny must be a strictly positive value");
  if (W <= 0.0) throw new ArgumentException("W must be a strictly positive value");
  if (H <= 0.0) throw new ArgumentException("H must be a strictly positive value");

  var cells = new List<Rectangle3d>(Nx * Ny);
  var values = new List<double>();
  var histogram = new List<double>();

  double h = 0.0;
  for(int i = 0; i < Nx; i++)
    for (int j = 0; j < Ny; j++)
    {
      double x0 = origem.X + (i * W);
      double x1 = x0 + W;
      double y0 = origem.Y + (j * H);
      double y1 = y0 + H;

      var cell = new Rectangle3d(Plane.WorldXY, new Interval(x0, x1), new Interval(y0, y1));
      int count = 0;

      foreach (Point3d p in pts)
        if ((p.X >= x0) && (p.X < x1) && (p.Y >= y0) && (p.Y < y1))
          count++;

      h = (1.0 / pts.Count) * (count / cell.Area);
      histogram.Add(h);
      values.Add(count);
      cells.Add(cell);
    }

  A = cells;
  B = values;
  C = histogram;
}

#8

Yes, and there is another annoying bug which is probably related. Every time you close and reopen the editor on the Mac side it changes the indentation of the first line of the code. Also there isn’t line numbering on the Mac editor and there is no code markup.


#9

Thanks! Its great to know!

The histogram value is a density estimation. Here is a good explanation (pages 5 and 6). Actually, I’m working with KDE but just took some time to implement the histogram for comparison purposes.

My current best shot at an implementing KDE in grasshopper is this C#_KDE_Silverman_FinalVersion.gh (112.1 KB), if you are interested.


(David Rutten) #10

Yeah I’m pretty sure they couldn’t use the same code editor on Mac we were using on Windows. We are trying to come up with a good cross-platform solution for all code editing controls in Rhino Win+Mac for the next version. Something that has good autocompletion, indentation, syntax highlighting, and support for all the languages we need.


(David Rutten) #11

Incidentally if you start to get performance problems when you grow the number of points and the number of cells, there’s a way to improve the runtime of your algorithm. Right now you’re iterating over all rows in the grid, then for all columns, then for all points, so your runtime is O(r \cdot c \cdot n). If you first make your entire grid of cells, and then iterate over all points and for each point figure out which cell index it belongs to (should be a fairly simple subtraction+division+rounding per point coordinate) you can get to O_1(r \cdot c) + O_2(n).


(qythium) #12

This happens to me all the time, to the point that I always copy the properly-newlined code out into a text panel when editing C# code on a Mac, in case it needs restoring.
And if there’s a single line of comments the entire thing becomes invalid syntax.
(Probably a Unix \n vs Windows \r\n encoding issue?)


Bugs - Grasshopper for MAC
(David Rutten) #13

Yeah in all likelihood, we’ll just have to decide who’s in charge of the fix. Probably both platforms should be able to handle newlines written in non-standard ways.


(qythium) #14

So I discovered this really hacky method of getting around the newline issue (which did turn out to be a \n vs \r\n issue, and happens to the VBScript editor also).

Just define the following function and call it anywhere within the C# script - It will then edit its own source code(!), removing leading / trailing spaces and converting newlines.

public void FormatNewlines(){
    var src = Grasshopper.Utility.InvokeGetterSafe(this.Component, "ScriptSource");
    if (src == null) return;
    String scriptCode = Grasshopper.Utility.InvokeGetterSafe(src, "ScriptCode") as String;
    String additionalCode = Grasshopper.Utility.InvokeGetterSafe(src, "AdditionalCode") as String;
    scriptCode = System.Text.RegularExpressions.Regex.Replace(scriptCode.Trim(), "(?<!\r)\n", "\r\n");
    additionalCode = System.Text.RegularExpressions.Regex.Replace(additionalCode.Trim(), "(?<!\r)\n", "\r\n");
    Grasshopper.Utility.InvokeSetterSafe(src, "ScriptCode", scriptCode);
    Grasshopper.Utility.InvokeSetterSafe(src, "AdditionalCode", additionalCode);
}

Alternatively, you could have a dedicated component that iterates over all the GH_ActiveObjects in the document and calls this function on each of them: Fix CRLF.gh (5.9 KB)

(I’m really surprised this even works :open_mouth:)