Rhino.Geometry.PointCloud RhinoCommon feature request

Hello All,

I feel that the Rhino.Geometry.PointCloud class could be improved. For example:

1- The constructor is lacks options. The user can only construct a PointCloud object with point information - no normal, no color, no value.

2- The AddRange function lacks options. The user cannot add only points and extra values - or can the user use the System.Void AddRange(IEnumerable points, IEnumerable normals, IEnumerable colors, IEnumerable values) function, giving null to the other enumerables? If that is the case, then just one AddRange function with better description would suffice.

It would be nice to have these available.

Thanks!

Hi @scudelari,

Does calling PointCloud.AppendNew from inside a loop help?

– Dale

Hi Dale,

For the basic cases, yes. But the issue is that you need really speed when you are dealing with point clouds.

I didn’t test it, but I have reasons to believe that it wouldn’t be applicable for large clouds because:
1- Likely the construction of a https://developer.rhino3d.com/api/rhinocommon/rhino.geometry.pointclouditem would be an important overhead.
2- Likely it allocates memory at the internal arrays for normals, colors and values when perhaps the user doesn’t need.
3- It would, I suppose, make a marshalled round-trip for each and every point and subvalue change.

As an example, I found out yesterday that the https://developer.rhino3d.com/api/rhinocommon/rhino.geometry.pointcloud/removerange is slow. I reckon that internally it does not recreate the C++ arrays, but changes it for every item that is to be removed.

I had to write this code which is much, much faster. But note that then one case is missing at the end - the one where the cloud only has points and values.

    public static PointCloud RemoveIndices_Fast(this PointCloud pc, int[] indices)
    {
      if (indices == null || indices.Length == 0) return pc;

      Point3d[] originalPoints = pc.GetPoints();

      Point3d[] subPoints = new Point3d[pc.Count - indices.Length];
      {
        int dest_i = 0;
        int ignore_i = 0;
        for (int orig_i = 0; orig_i < originalPoints.Length;)
        {
          if (orig_i == indices[ignore_i]) // This index must be ignored
          {
            ignore_i++;
          }
          else
          {
            subPoints[dest_i] = originalPoints[orig_i];
            dest_i++;
          }
          orig_i++;
        }
      }

      Vector3d[] subNormals = null;
      if (pc.ContainsNormals) // Has normals
      {
        Vector3d[] original = pc.GetNormals();
        subNormals = new Vector3d[pc.Count - indices.Length];
        int dest_i = 0;
        int ignore_i = 0;
        for (int orig_i = 0; orig_i < originalPoints.Length;)
        {
          if (orig_i == indices[ignore_i]) // This index must be ignored
          {
            ignore_i++;
          }
          else
          {
            subNormals[dest_i] = original[orig_i];
            dest_i++;
          }
          orig_i++;
        }
      }

      Color[] subColors = null;
      if (pc.ContainsColors) // Has colours
      {
        Color[] original = pc.GetColors();
        subColors = new Color[pc.Count - indices.Length];
        int dest_i = 0;
        int ignore_i = 0;
        for (int orig_i = 0; orig_i < originalPoints.Length;)
        {
          if (orig_i == indices[ignore_i]) // This index must be ignored
          {
            ignore_i++;
          }
          else
          {
            subColors[dest_i] = original[orig_i];
            dest_i++;
          }
          orig_i++;
        }
      }

      double[] subValues = null;
      if (pc.ContainsPointValues) // Has values
      {
        double[] original = pc.GetPointValues();
        subValues = new double[pc.Count - indices.Length];
        int dest_i = 0;
        int ignore_i = 0;
        for (int orig_i = 0; orig_i < originalPoints.Length;)
        {
          if (orig_i == indices[ignore_i]) // This index must be ignored
          {
            ignore_i++;
          }
          else
          {
            subValues[dest_i] = original[orig_i];
            dest_i++;
          }
          orig_i++;
        }
      }

      PointCloud subPc = new PointCloud();
      // Try only one call with nulls. TRIED - didn't work.
      if (subNormals == null && subColors == null && subValues == null) subPc.AddRange(subPoints);
      else if (subNormals != null && subColors == null && subValues == null) subPc.AddRange(subPoints, subNormals);
      else if (subNormals == null && subColors != null && subValues == null) subPc.AddRange(subPoints, subColors);
      else if (subNormals != null && subColors != null && subValues == null) subPc.AddRange(subPoints, subNormals, subColors);
      else if (subNormals != null && subColors != null && subValues != null) subPc.AddRange(subPoints, subNormals, subColors, subValues);

      return subPc;
    }

Of course, perhaps I am saying bullocks here because I don’t have access to your code to know exactly how things are done.

Thanks

Hi @scudelari,

I’ve logged an issue for the PointCloud.RemoveRange slowness.

https://mcneel.myjetbrains.com/youtrack/issue/RH-76488

I’ll look into adding a few more constructors and creation functions.

– Dale

RH-76488 is fixed in the latest WIP

1 Like

@brian Thank you, and it keeps amazing me how much you guys are efficient on helping out the outside developers who use your tools.

2 Likes