Rolf would you mind explaining this in more “cleaner” manner for me? I don’t get this concept at first glance
Intersecting a vertical surface (or BrepFace) is essentially the same idea as intersecting a line from a center point to any of the point in the “cloud”. In this image one point (to the right of the cp is identified as “inside” the curve ( = no intersections found between the center point and the point)
Fig 1. Curve between points intersecting the brepFace - test
If the curve had been concave, the curve may intersect the surface several times (just like in the case of a Line or a Ray intersection) and due to the array of intersection points you can figure out if a point is inside, by comparing distances to the points in the array (but only betwee odd-even pairs (if the starting point is included), otherwise just shift odd-even.
Fig 2. Elaborate illustration
If a point is located between (cp = A) and a first intersection point (0), then it is inside, or, any point located between odd-even number of intersection points is inside (like point B, whcih is between 1 and 2. This is the “modulo 2 = 0”-check I did in the earlier posted script.
// Rolf
@RIL
I’m not quite sure if that logic is rigorous enough to cover all possible closed curves - how do you define the ‘center point’ and ensure that it is inside the curve? Using simple area centroid would fail for eg. a ‘C’ shaped curve.
The most common algorithm (which you might have had in mind) is ray-casting, which projects a semi-infinite line in a random direction from your test point and counts the number of intersections with the curve, see https://en.wikipedia.org/wiki/Point_in_polygon
RhinoCommon probably uses some version of this algorithm under the hood for its Curve.Contains() method, so I doubt you would see any performance improvements by replicating it outside… In fact I was surprised that the Brep Find-Closest-Points version tested so much faster, which could point at some further optimizations being made there.
Some off-the-bat suggestions:
Have you tried parallelizing using System.Threading.Tasks.Parallel.ForEach
and ConcurrentStack<T>
?
Skip checking for all 200 curves if you’re only interested in the ‘topmost’ one that contains the point, by breaking out of the loop as soon as a match is found
I didn’t elaborate on how to find a “center point” since I didn’t know what contour curves would be involved. So I simply assumed a starting point, of which a center point perhaps would be an option. But “starting point” would of course have been a better name since it was unknown to me from start whether a (area) centerpoint or centroid would be an option.
However, there’s no big problem to calulate a starting point inside the area. Depending on the type of curve (closed or multiple open curves, one could pick two points at parametrized .25 and .75 on a closed curve, or at 0.5 at any two curves if more than one, and then perform the exact same test as described above. Any point closer than the first intersection is inside, and any point closer than the farther point in any following odd-even pairs is also inside the curve. And from there you do similar checks for the rest of the other points.
But whether this approach is faster than any other strategy I have no idea (since I have not tested it against any other strategy). The only thing I know is that it doesn’t take much to beat the CLX component, which takes forever.
// Rolf
OK, now I have read the linked WP article (https://en.wikipedia.org/wiki/Point_in_polygon).
I didn’t know that it was a documented method (I have never seen it anywhere, and I didn’t search for it either). Nor did I know that it had a fancy name, and that there’s even some “theoremes” related to it. I hacked it on the fly and thought it seemed like a reasonable way of solving the problem. Obviously others had the same idea long before me.
// Rolf
Here is C# component that does the job.
Performance -> 35666 points check against 166 curves in less then one second.
Code is short and explained inside component.
PointInInsideCurve.gh (754.6 KB)
R
@RadovanG Thanks for involvement! But it seems it doesn’t do the job at all here
@qythium I already have:
if (!pts.Any())
break;
@RIL Rolf here is some interesting stuff about that http://alienryderflex.com/polygon/
But probably i should understand rtree concept cause given set of points won’t change during crvs parameter changes.
Hi.
Can you post/internalise input curves and points.
Radovan
@RadovanG here you are Transformed ones are for shape with index 2
intern_pts shps transf.gh (240.5 KB)
I was also wondering if i couldn’t cast final transformation to bitmap and pick those points via texture evaluator. This would be overkill for curves but all points would only iterate once.
@nathanletwory is there any way to bake and hold image inside memory and use it internally without saving it and picking again for rdk texture evaluator?
With RhinoCommon in v6 I think you should be able to create a custom RenderTexture in code, with associated TextureEvaluator.
Hi
If I test my c# component with your 200 curves and 10000 points i got this on my laptop (i7-7700HQ 16GB ram Win10).
@RadovanG that is indeed weird - in separate file i got 348ms on my laptop (i7-6700HQ 32GB ram Win10) but when it comes to comparison there’s no big improvement on parallel cause on this set of pt and crvs and additional parameters i got ~50 - 70 ms more.
AD 1
@RadovanG i’ve pasted your and my c# component to new file both behaves better and improvement is marginal ( note: i don’t use any parallel method) - Besides it’s still ~3 fps
There should be difference in parallel vs serial for loop if there is reasonable eanugh points to iterate. I have check it for 30 x 10000 same points (total 300.000 points) both with parallel and seraila for loop and:
Parallel
No Parallel
@RadovanG i don’t say there’s no difference i just say that i don’t see any improvement on 10k and 200crvs - on 500 crvs parallel is slower - but you are using crv methods i use meshray here - and this isn’t still solution for me cause 300ms isn’t responsive in terms of user dealing with normal UI for fine tuning stuff
Here’s my attempt using parallel meshing and RTrees
(compared to @RadovanG’s version on top)
PointInInsideCurve v2.gh (247.5 KB)
Tested on quadcore i7 2.7GHz, 16GB RAM laptop
Code for anyone interested:
private void RunScript(List<Curve> crvs, List<Point3d> pts, System.Object treeobj, ref object A, ref object B)
{
double tol = 0.01;
var meshStack = new System.Collections.Concurrent.ConcurrentStack<Mesh>();
System.Threading.Tasks.Parallel.ForEach(crvs, (Curve crv) => meshStack.Push(Mesh.CreateFromPlanarBoundary(crv, MeshingParameters.Minimal)));
Mesh[] meshes = meshStack.ToArray();
RTree meshTree = new RTree();
for(int i = 0; i < meshes.Count(); i++){
meshTree.Insert(meshes[i].GetBoundingBox(true), i);
}
Ray3d[] rays = pts.Select(pt => new Ray3d(pt, Vector3d.ZAxis)).ToArray();
RTree ptTree = treeobj as RTree; // treeobj = RTree.CreatePointCloudTree(new PointCloud(pts));
var datatree = new DataTree<Point3d>();
var includedPts = new HashSet<int>();
EventHandler < RTreeEventArgs > handler = ((object sender, RTreeEventArgs e) => {
if(includedPts.Contains(e.Id)) return;
Point3d pt = pts[e.Id];
if (Rhino.Geometry.Intersect.Intersection.MeshRay(meshes[e.IdB], rays[e.Id]) >= 0.0){
includedPts.Add(e.Id);
datatree.Add(pt, new GH_Path(e.IdB));
}
});
RTree.SearchOverlaps(ptTree, meshTree, tol, handler);
A = datatree;
B = meshes;
}
Are all your curves convex?
I can’t say user will decide cause user will pick own crv
@qythium hmm weird i got 100ms on tree and 200ms on point picking - i’ll try to restart system
in v6 tree takes 377 and pick 199ms however behaves wayyy more fluid
on dual xeon nothing better
@qythium And many thanks for showing rtree use on this!
@RadovanG good that you pointed out this but yes we discussed this earlier - and it depends on mesh quality which can be always changed but for my purpose i don’t mind with 10pt loss in 10k set - those points will be still included but just in different subset/color
@RIL @qythium @RadovanG
Very interesting is that picking texture is even slower than all our previous attempts