TBC Macros and Extensions

 View Only
Expand all | Collapse all

built-in way to retrieve certain points from cloud and potential bug in Hoops2dView.PointCloudPick

  • 1.  built-in way to retrieve certain points from cloud and potential bug in Hoops2dView.PointCloudPick

    Posted 2 days ago
    Edited by Ronny Schneider 2 days ago
      |   view attached

    Hi,

    what's the most performant way to get a list of Point3D's either within a certain radius or box around a given Point3D (not station), assuming I have list of GUID's of all currently visible cloud regions.

    To get that center point I use Hoops3dView.PointCloudPick, that works in 3D. But in planview the Hoops2dView.PointCloudPick always returns NaN. Is this a bug?

    I'm currently building my own cloudpoint Octree during macro startup and after a ViewfilterChange. That is time consuming at first but works reasonably well during runtime.

    Box filtering is quite a bit faster than circular.



    ------------------------------
    Ronny Schneider
    ------------------------------



  • 2.  RE: built-in way to retrieve certain points from cloud and potential bug in Hoops2dView.PointCloudPick

    Posted 2 days ago

    Hey Ronny, 
    Any chance you have played with the FilterByBox in the Sde library?

    Something like this. 

    // Given: Point3D center, double halfSize, IPointCloudRegion region
    
    // 1. Get the SDE CloudId from the region integration
    var integration = region.Integration.Provide<IPointCloudRegionIntegration>();
    var cloudId = integration.Provide<Sde.CloudId>();
    if (cloudId == null) return;
    
    // 2. Build an axis-aligned box filter around your center point
    var origin = new Point3D(
        center.X - halfSize,
        center.Y - halfSize,
        center.Z - halfSize);
    var axisX = new Vector3D(2 * halfSize, 0, 0);
    var axisY = new Vector3D(0, 2 * halfSize, 0);
    var axisZ = new Vector3D(0, 0, 2 * halfSize);
    
    var boxFilter = new Sde.FilterByBox(
        origin.ToSde(), axisX.ToSde(), axisY.ToSde(), axisZ.ToSde());
    
    // 3. Create a filtered Cloud - SDE handles spatial culling internally
    using (var cloud = new Cloud(cloudId))
    using (var filtered = new Cloud(cloud, boxFilter, SdePointSource.Full, out var exclusion))
    {
        exclusion?.Dispose();
    
        var results = new List<Point3D>();
        foreach (SdePointInfo pt in filtered)
        {
            results.Add(pt.FromSde());
        }
    }


    ------------------------------
    Bryce Haire
    ------------------------------



  • 3.  RE: built-in way to retrieve certain points from cloud and potential bug in Hoops2dView.PointCloudPick

    Posted yesterday
    Edited by Ronny Schneider yesterday

    Hello Bryce,

    thanks for the code.

    no, I wasn't aware of it and how to use it, and it doesn't seem to be straight forward as the error below shows.

    It's not a standard Trimble.Sdk assembly and mostly undocumented in the object browser.

    We still haven't got any decent documentation on how the pointcloud database works internally and how everything is intertwined.

    The last reasonably decent documentation we received was for IFC objects, and that dates more than 1 year back, https://community.trimble.com/viewdocument/2023-2024-bimifc-schema-update?CommunityKey=8a262af4-a35e-4e9a-9dd3-191cc785899a&tab=librarydocuments

    There are database files, guids, the standard serial numbers, several types of integrations???, what does SDE stand for, spatial data engine?? ......

    A decent tree-like view would be good for this.

    And please keep in mind that you're advertising the macro language to be used with IronPython, no help/samples/documentation/templates are provided to use C#.

    A lot of the above code doesn't work straight away in IronPython. I had to rely heavily on a chatbot. I finally got to the point where I do get a filter result, but now I'm stuck on looping over the filtered list. 

    So far, I've got to this point

                closesttocursor = self.activeForm.View.PointCloudPick(e.MousePosition.X, e.MousePosition.Y, False)
    
                # for some reason those SDE classes are stored in a separate assembly
                clr.AddReference ("SDE.NET")
                from Trimble.Sde import Cloud as SdeCloud, FilterByBox as SdeFilterByBox, CloudId as SdeCloudId, SdePointSource
    
                # visibleclouds contains region objects of type  Trimble.Vce.Data.Scanning.ExposedPointCloudRegion or DefaultExposedPointCloudRegion
                # in IronPython the <> need to be []
                cloudId = self.visibleclouds[0].Integration.Provide[SdeCloudId]()
                cloud = SdeCloud(cloudId)
    
                # casting with an extension like ToSde() doesn't work in IronPython
                # either import
                # from System.Windows.Media.Media3D import Point3D as SdePoint3D, Vector3D as SdeVector3D
                # and build the objects manually
                # or import Point3DExtensions
                # problem with those is that they are spread over multiple assemblies, you need to add the proper reference
                # Point3DExtensions.ToSde is in Namespace "Trimble.Vce.Geometry" but inside "Trimble.Vce.Scanning"
                # if you reference "Trimble.Vce.Geometry" only you won't have access to the methods in Trimble.Vce.Scanning->Namespace:Trimble.Vce.Geometry.Point3DExtensions
                # but once you reference "Trimble.Vce.Scanning" it's enough to import from "Trimble.Vce.Geometry"
    
                clr.AddReference ("Trimble.Vce.Scanning")
                from Trimble.Vce.Geometry import Point3DExtensions
                # now you can do the following
    
                r = self.captureradius.Distance
                boxorigin = Point3DExtensions.ToSde(Point3D(closesttocursor.X - r, closesttocursor.Y - r, closesttocursor.Z - r))
                # or build the correct type objects manually, as for instance the vectors below; is probably simpler than finding the assembly the extensions are hidden in
                boxfilter = SdeFilterByBox(boxorigin, SdeVector3D(2*r, 0, 0), SdeVector3D(0, 2*r, 0), SdeVector3D(0, 0, 2*r))
    
                exclusion = clr.StrongBox[SdeCloud]()
                filtered = SdeCloud(cloud, boxfilter, SdePointSource.Full, exclusion) # and here it fails, it wants more arguments
    

    it fails on the last step with "expected IntPtr, got NoneType".

    From the VS object browser

    Cloud(Trimble.Sde.Cloud sourceCloud, Trimble.Sde.Filter filter, Trimble.Sde.SdePointSource pointSource,
    out Trimble.Sde.Cloud filterExclusion, Trimble.Sde.ProgressionDelegate progressionCallback = null, IntPtr userData = null)

    it seems to ignore the missing "ProgressionDelegate", but insists to get the "IntPtr"

    using

                filtered = SdeCloud(cloud, boxfilter, SdePointSource.Full, exclusion, None, None)

    it complains about NoneTypes

    using IntPtr with an arbitrary number works, why does it need this pointer? what is this pointer?

                filtered = SdeCloud(cloud, boxfilter, SdePointSource.Full, exclusion, None, IntPtr(0))

    But now I'm unable to loop over the filtered list. It says it contains some points, but the enumerator is empty. I have the feeling this is using some extension again.

    I tried

    filtered.GetEnumerator()

    but doesn't work either



    ------------------------------
    Ronny Schneider
    ------------------------------



  • 4.  RE: built-in way to retrieve certain points from cloud and potential bug in Hoops2dView.PointCloudPick

    Posted yesterday
    Edited by Ronny Schneider yesterday

    I found the issue with the enumerator.

    It's an absolute MUST to dispose of the cloud object, otherwise something gets completely messed up internally. It looked as if all enumerating/looping in any kind of list got broken.

    Only a TBC restart fixed it.

    This works

                boxorigin = Point3DExtensions.ToSde(Point3D(closesttocursor.X - r, closesttocursor.Y - r, closesttocursor.Z - r))
                # or build the correct type objects manually, as for instance the vectors below; is probably simpler than finding the assembly the extensions are hidden in
                boxfilter = SdeFilterByBox(boxorigin, SdeVector3D(2*r ,0, 0), SdeVector3D(0, 2*r, 0), SdeVector3D(0, 0, 2*r))
                
                for region in self.visibleclouds:
                    # visibleclouds contains region objects of type  Trimble.Vce.Data.Scanning.ExposedPointCloudRegion or DefaultExposedPointCloudRegion
                    # in IronPython the <> need to be []
                    #cloudId = region.Integration.Provide[SdeCloudId]()
                    cloud = SdeCloud(region.Integration.Provide[SdeCloudId]())
          
                    
                    exclusion = clr.StrongBox[SdeCloud]()
                    filtered = SdeCloud(cloud, boxfilter, SdePointSource.Full, exclusion, None, IntPtr(0))
                    exclusion.Dispose() # !!!!!! super important
                    for p in filtered:
                        self.overlayBag.AddMarker(Point3D(p.Coordinates.X, p.Coordinates.Y, p.Coordinates.Z), GraphicMarkerTypes.BigDot_IndependentColor, Color.Orange.ToArgb(), "", 0, 0, 1.0)
    
                    cloud.Dispose() # !!!!!! super important
    

    Is quite a bit slower than my own Octree approach. That one needs quite some time to create the lookup table but seems much faster for basic box filtering where the box axes are aligned to world.
    ------------------------------
    Ronny Schneider
    ------------------------------



  • 5.  RE: built-in way to retrieve certain points from cloud and potential bug in Hoops2dView.PointCloudPick

    Posted yesterday
      |   view attached

    Blue selection is using my Octree. That one needs as uncompiled Python script 18 sec to populate the 875000 points, but refresh rate is much better, at least with an axes aligned simple box. With a circle it gets similar to the built-in rate.

    Orange selection is with FilterByBox.



    ------------------------------
    Ronny Schneider
    ------------------------------