===== Point Cloud Tools in XL and Headless Mode ===== **Note:** This tutorial is based on the plugin "PointCloud" that you can get either through the Plugin manager (within Groimp) or by downloading the jar [[https://gitlab.com/grogra/groimp-plugins/pointcloud|here]]. This overview contains the list of available java functions concerning point cloud import, clustering, splitting, merging, and export. They can be used in the java code (by future developers) and in XL code (by GroIMP users). They can also be used in headless mode. Parameters are always passed when the functions are called in code, they are never requested in a graphical window. All functions can be used when the class ''de.grogra.imp3d.pointcloud.PointCloudTools'' is imported with the following java/XL command: import de.grogra.imp3d.pointcloud.PointCloudTools; All functions are **static** functions in the ''PointCloudTools'' class and **not** available as object based functions like ''pointcloud.function()''. The given point clouds and returned point clouds are of the type ''de.grogra.imp3d.objects.PointCloud'' and fully compatible to the other 3D objects. All objects that are created by functions in the ''PointCloudTools'' class are **not** added to the RGG graph. They only exist as objects in the code context and can be added to the graph manually if required. ==== Importing point clouds ==== Point clouds can be imported from text files. The file path is expected as a string object and can be declared as a relative or absolute path. This function returns an array with all point clouds from the file. If the file does not contain point cloud ID information (and only declares one point cloud), the array will contain exactly one point cloud. **Declaration in PointCloudTools.java:** public static PointCloud[] importFromFile(String file) { ... return importedPointClouds; } **XL code example for multiple point clouds:** String file = "tomato_plants.xyz"; PointCloud[] tomatoPlants = PointCloudTools.importFromFile(file); **XL code example for one point cloud:** *If only one point cloud is stored in the file, the point cloud has the index* ''0''. String file = "tomato_plant.xyz"; PointCloud[] imported = PointCloudTools.importFromFile(file); PointCloud tomatoPlant = imported[0]; ==== Clustering point clouds ==== The clusterization of point clouds requires some parameters. More information about the parameters ''epsilon'', ''minimumNeighbors'' and ''octreeDepth'' can be found in the implementation part of this document. This function returns an array of newly created point clouds. The given point cloud is not affected. If the ''removeNoiseCluster'' parameter is set to ''true'', points with a too low number of neighbor points (= noise) are removed during the clusterization. If the parameter is set to ''false'', an additional cluster (point cloud) is added to the end of the point cloud array. The additional point cloud contains all noise points. If the noise cluster is added and there are no noise points, the noise cluster remains in an empty point cloud. **Declaration in PointCloudTools.java:** public static PointCloud[] cluster(PointCloud pointCloud, double epsilon, int minimumNeighbors, int octreeDepth, boolean removeNoiseCluster) { ... return pointCloudClusters; } **XL code example:** PointCloud tomatoPlant = ...; double epsilon = 0.003; int minimumNeighbors = 4; int octreeDepth = 10; boolean removeNoiseCluster = false; PointCloud[] clusters = PointCloudTools.cluster(tomatoPlant, epsilon, minimumNeighbors, octreeDepth, removeNoiseCluster); ==== Splitting a point cloud ==== A point cloud can be split by a plane. The plane should have a position and a rotation so that the concerning point cloud can be split. This function returns an array with two point clouds. If the plane does not cut the point cloud, one of the resulting point clouds will be empty. This function does not affect the given objects. **Declaration in PointCloudTools.java:** public static PointCloud[] split(PointCloud pointCloud, Plane plane) { ... return new PointCloud[]{frontPointCloud, backPointCloud}; } **XL code example:** PointCloud twoPlants = ...; Plane plane = new Plane(); plane.setRotate(0, Math.PI/2, 0);// vertical PointCloud[] frontAndBackPointClouds = PointCloudTools.split(twoPlants, plane); PointCloud firstPlant = frontAndBackPointClouds[0]; PointCloud secondPlant = frontAndBackPointClouds[1]; ==== Merging point clouds ==== Multiple point clouds can be merged to a large one. This function returns a new point cloud with cloned points and does not affect the given objects. The source point cloud of each point is not stored. **Declaration in PointCloudTools.java:** public static PointCloud merge(PointCloud[] pointClouds) { ... return mergedPointCloud; } **XL code example:** PointCloud[] lotsOfTooDetailedPointClouds = ...; PointCloud unionSet = PointCloudTools.merge(lotsOfTooDetailedPointClouds); ==== Exporting point clouds ==== A certain selection of point clouds can be exported into a text file. The targeted point clouds must be provided as array and are not affected during the export. The file path is expected as a string object and can be declared as a relative or absolute path. If there is only one point cloud in the given array, the point cloud ID information is not stored in the exported file. If there are multiple point clouds in the array, the index of the point cloud in the array is used as point cloud ID and added to each point in the file. **Declaration in PointCloudTools.java:** public static void exportToFile(PointCloud[] pointClouds, String file) { PointCloudTools.writePointCloudFile(pointClouds, file); } **XL code example for multiple point clouds:** PointCloud[] clustersOfPlant = ...; String file = "plant_clusters.xyz"; PointCloudTools.exportToFile(clustersOfPlant, file); **XL code example for one point cloud:** *To export one point cloud, an array with one element must be created.* PointCloud[] export = new PointCloud[1]; export[0] = pointCloud; String file = "plant.xyz"; PointCloudTools.exportToFile(export, file); --- This overview contains the list of available java functions concerning point cloud fitting (to spheres, cylinders, frustums, and cones. They can be used in the java code (by future developers) and in XL code (by GroIMP users). They can also be used in headless mode. Parameters are always passed when the functions are called in code, they are never requested in a graphical window. All functions can be used when the class ''de.grogra.imp3d.pointcloud.PointCloudTools'' is imported with the following java/XL command: import de.grogra.imp3d.pointcloud.PointCloudTools; All functions are **static** functions in the ''PointCloudTools'' class and **not** available as object based functions like ''pointcloud.function()''. The given point clouds and returned 3D objects are always of the following types and fully compatible to the other objects in the 3D view: * ''de.grogra.imp3d.objects.PointCloud'' * ''de.grogra.imp3d.objects.Sphere'' * ''de.grogra.imp3d.objects.Cylinder'' * ''de.grogra.imp3d.objects.Frustum'' * ''de.grogra.imp3d.objects.Cone'' * ''de.grogra.graph.impl.Node'' All objects that are created and returned by java functions and called in XL scripts, are **not** automatically added to the RGG graph. They only exist as objects in the code context and can be added to the graph manually if required. The java functions that can be called in XL scripts exist twice. Each function can be used to fit one point cloud to one object or to fit an array of point clouds to an array of objects. All of them are listed below: Parameters and return values are not described in detail here. More information about how to choose the right parameter values is given in the chapter about the graphical functions. ==== Fitting spheres to point clouds ==== With the following two functions, spheres can be fitted to point clouds. If the first one is used, one point cloud is required and one sphere is returned. By using the second function, an array of point clouds must be provided and an array of spheres is returned. **Declaration in PointCloudTools.java:** // Fitting one sphere to one point cloud public static Sphere fitSphereToPointCloud(PointCloud pointCloud, boolean average) { ... return sphere; } // Fitting multiple spheres to multiple point clouds public static Sphere[] fitSpheresToPointClouds(PointCloud[] pointClouds, boolean average) { ... return spheres; } **XL code example:** boolean average = true; // Fitting one sphere to one point cloud PointCloud pointCloud = ...; Sphere sphere = PointCloudTools.fitSphereToPointCloud(pointCloud, average); // Fitting multiple spheres to multiple point clouds PointCloud[] pointClouds = ...; Sphere[] spheres = PointCloudTools.fitSpheresToPointClouds(pointClouds, average); ==== Fitting cylinders to point clouds ==== With the following two functions, cylinders can be fitted to point clouds. If the first one is used, one point cloud is required and one cylinder is returned. By using the second function, an array of point clouds must be provided and an array of cylinders is returned. **Declaration in PointCloudTools.java:** // Fitting one cylinder to one point cloud public static Cylinder fitCylinderToPointCloud(PointCloud pointCloud, boolean average, int precision) { ... return cylinder; } // Fitting multiple cylinders to multiple point clouds public static Cylinder[] fitCylindersToPointClouds(PointCloud[] pointClouds, boolean average, int precision) { ... return cylinders; } **XL code example:** boolean average = true; int precision = 1000; // Fitting one cylinder to one point cloud PointCloud pointCloud = ...; Cylinder cylinder = PointCloudTools.fitCylinderToPointCloud(pointCloud, average, precision); // Fitting multiple cylinders to multiple point clouds PointCloud[] pointClouds = ...; Cylinder[] cylinders = PointCloudTools.fitCylindersToPointClouds(pointClouds, average, precision); ==== Fitting frustums to point clouds ==== With the following two functions, frustums can be fitted to point clouds. If the first one is used, one point cloud is required and one frustum is returned. By using the second function, an array of point clouds must be provided and an array of frustums is returned. **Declaration in PointCloudTools.java:** // Fitting one frustum to one point cloud public static Frustum fitFrustumToPointCloud(PointCloud pointCloud, boolean average, int precision) { ... return frustum; } // Fitting multiple frustums to multiple point clouds public static Frustum[] fitFrustumsToPointClouds(PointCloud[] pointClouds, boolean average, int precision) { ... return frustums; } **XL code example:** boolean average = true; int precision = 1000; // Fitting one frustum to one point cloud PointCloud pointCloud = ...; Frustum frustum = PointCloudTools.fitFrustumToPointCloud(pointCloud, average, precision); // Fitting multiple frustums to multiple point clouds PointCloud[] pointClouds = ...; Frustum[] frustums = PointCloudTools.fitFrustumsToPointClouds(pointClouds, average, precision); ==== Fitting cones to point clouds ==== With the following two functions, cones can be fitted to point clouds. If the first one is used, one point cloud is required and one cone is returned. By using the second function, an array of point clouds must be provided and an array of cones is returned. **Declaration in PointCloudTools.java:** // Fitting one cone to one point cloud public static Cone fitConeToPointCloud(PointCloud pointCloud, boolean average, int precision) { ... return cone; } // Fitting multiple cones to multiple point clouds public static Cone[] fitConesToPointClouds(PointCloud[] pointClouds, boolean average, int precision) { ... return cones; } **XL code example:** boolean average = true; int precision = 1000; // Fitting one cone to one point cloud PointCloud pointCloud = ...; Cone cone = PointCloudTools.fitConeToPointCloud(pointCloud, average, precision); // Fitting multiple cones to multiple point clouds PointCloud[] pointClouds = ...; Cone[] cones = PointCloudTools.fitConesToPointClouds(pointClouds, average, precision); ==== Fitting automatically detected objects to point clouds ==== With the following two functions, automatically selected objects can be fitted to point clouds. If the first one is used, one point cloud is required and one node object is returned. By using the second function, an array of point clouds must be provided and an array of node objects is returned. **Note:** These functions return ''Node'' objects. ''Node'' is the super class of ''Sphere'', ''Cylinder'', ''Frustum'', and ''Cone'' (and all other 3D objects in XL, but that is not important here). This means that a ''Node'' object is like a "category" type for the other types and the returned node is of one of the other types implicitly. The algorithm decides automatically which of these fits best to the given point cloud. If multiple point clouds are given to the automatic function, different types of objects can be contained in the resulting array of ''Node'' objects. **Declaration in PointCloudTools.java:** // Fitting one object to one point cloud public static Node fitAutomaticObjectToPointCloud(PointCloud pointCloud, boolean average, int precision) { ... return node; } // Fitting multiple objects to multiple point clouds public static Node[] fitAutomaticObjectsToPointClouds(PointCloud[] pointClouds, boolean average, int precision) { ... return nodes; } **XL code example:** boolean average = true; int precision = 1000; // Fitting one object to one point cloud PointCloud pointCloud = ...; Node node = PointCloudTools.fitAutomaticObjectToPointCloud(pointCloud, average, precision); // Fitting multiple objects to multiple point clouds PointCloud[] pointClouds = ...; Node[] nodes = PointCloudTools.fitAutomaticObjectsToPointClouds(pointClouds, average, precision); The following code example shows how the type can be detected in XL. **XL code example:** PointCloud pointCloud = ...; boolean average = true; int precision = 1000; Node node = PointCloudTools.fitAutomaticObjectToPointCloud(pointCloud, average, precision); if (node instanceof Sphere) { Sphere sphere = (Sphere)(node); // Do anything with 'sphere' } else if (node instanceof Cylinder) { Cylinder cylinder = (Cylinder)(node); // Do anything with 'cylinder' } else if (node instanceof Frustum) { Frustum frustum = (Frustum)(node); // Do anything with 'frustum' } else if (node instanceof Cone) { Cone cone = (Cone)(node); // Do anything with 'cone' } else { // This case can not happen. 'node' is never null and never of an unknown type. } This also works with multiple point clouds. **XL code example:** PointCloud[] pointClouds = ...; boolean average = true; int precision = 1000; Node[] nodes = PointCloudTools.fitAutomaticObjectsToPointClouds(pointCloud, average, precision); int index = 0; while (index < nodes.length) { if (nodes[index] instanceof Sphere) { // Do the same distinction as in the example above, but with 'nodes[index]' } // ... index++; }