Examples
Creating a ccPointCloud from Python
import pycc
import laspy
import numpy as np
# This example shows how to create a ccPointCloud from your custom data
# here, we will load data from a las file using laspy.
# Note that the resulting ccPointCloud's point values and scalar field values
# will be copies.
path_to_las = r"L:\R1_F_0+000_0+050.las"
las = laspy.read(path_to_las)
# Be aware that CloudCompare stores coordinates on 32 bit floats.
# To avoid losing too much precision you should 'shift' your coordinates
# if they are 64 bit floats (which is the default in python land)
xs = (las.x - las.header.x_min).astype(pycc.PointCoordinateType)
ys = (las.y - las.header.y_min).astype(pycc.PointCoordinateType)
zs = (las.z - las.header.z_min).astype(pycc.PointCoordinateType)
point_cloud = pycc.ccPointCloud(xs, ys, zs)
# Add the global shift to CloudCompare so that it can use it,
# for example to display the real coordinates in point picking tool
point_cloud.setGlobalShift(-las.header.x_min, -las.header.y_min, -las.header.z_min)
point_cloud.setName(path_to_las)
print(point_cloud.size())
assert np.all(xs == point_cloud.points()[..., 0])
# Adding scalar field & copying values the manual way
idx = point_cloud.addScalarField("classification")
classification_array = point_cloud.getScalarField(idx).asArray()
classification_array[:] = las.classification[:]
print(classification_array)
# Or give the values directly
idx = point_cloud.addScalarField("intensity", las.intensity)
intensity_array = point_cloud.getScalarField(idx).asArray()
print(intensity_array[:])
try:
cc = pycc.GetInstance()
except AttributeError:
exit(0)
# We are in "embedded mode", add the point cloud to the DB
# but first we should compute min and max for the scalarfields
# so that their color ranges displays properly
point_cloud.getScalarField(point_cloud.getScalarFieldIndexByName("intensity")).computeMinAndMax()
point_cloud.getScalarField(point_cloud.getScalarFieldIndexByName("classification")).computeMinAndMax()
cc.addToDB(point_cloud)
cc.updateUI()
Using ccMesh
"""
An example of how to create a mesh or query a mesh
"""
import cccorelib
import pycc
import numpy as np
# Vertices of the cube
VERTICES = np.array([
[-0.5, -0.5, -0.5],
[-0.5, -0.5, 0.5],
[-0.5, 0.5, -0.5],
[-0.5, 0.5, 0.5],
[0.5, -0.5, -0.5],
[0.5, -0.5, 0.5],
[0.5, 0.5, -0.5],
[0.5, 0.5, 0.5]
])
# Indices of the cube
INDICES = np.array([
[0, 1, 3],
[0, 3, 2],
[0, 2, 4],
[2, 6, 4],
[0, 4, 1],
[1, 4, 5],
[2, 3, 6],
[3, 7, 6],
[4, 6, 5],
[5, 6, 7],
[1, 5, 7],
[1, 7, 3]
])
def main():
# Creating mesh
vertices = pycc.ccPointCloud(VERTICES[:, 0], VERTICES[:, 1], VERTICES[:, 2])
mesh = pycc.ccMesh(vertices)
for (i1, i2, i3) in INDICES:
mesh.addTriangle(i1, i2, i3)
assert vertices.size() == len(VERTICES)
assert mesh.size() == len(INDICES)
assert vertices == mesh.getAssociatedCloud()
# Query triangles
x, y, z = cccorelib.CCVector3(), cccorelib.CCVector3(), cccorelib.CCVector3()
for i in range(mesh.size()):
vert_indices = mesh.getTriangleVertIndexes(i)
assert vert_indices.i1 == INDICES[i, 0]
assert vert_indices.i2 == INDICES[i, 1]
assert vert_indices.i3 == INDICES[i, 2]
for j in range(3):
assert vert_indices[j] == INDICES[i, j]
vertices.getPoint(vert_indices.i1, x)
vertices.getPoint(vert_indices.i2, y)
vertices.getPoint(vert_indices.i3, z)
# or
mesh.getTriangleVertices(i, x, y, z)
CC = pycc.GetInstance()
CC.addToDB(mesh)
CC.updateUI()
if __name__ == '__main__':
main()
Loading a file
import pycc
CC = pycc.GetInstance()
params = pycc.FileIOFilter.LoadParameters()
params.parentWidget = CC.getMainWindow()
path = r"~\Projects\CloudCompare\plugins\private\CloudCompare-PythonPlugin\tests\data\a_cloud.bin"
# This automatically adds the loaded entities
# to the DB on success, and raises an exception if an error occured
obj = CC.loadFile(path, params)
# This does not add to the DB, and returns None if the loading failed
obj = pycc.FileIOFilter.LoadFromFile(path, params)
if obj is not None:
print("Failed to load the file")
CC.addToDB(obj)
params = pycc.FileIOFilter.SaveParameters()
result = pycc.FileIOFilter.SaveToFile(obj, r'savedFromPlugin.bin', params)
result = pycc.FileIOFilter.SaveToFile(obj.getChild(0), r'savedFromPlugin.laz', params)
Scalar Field
"""Example that shows how to add a scalar field
as well as how to make it properly be displayed in the UI
"""
import pycc
import numpy as np
xs = np.arange(0.0, 5.0, 0.1, pycc.PointCoordinateType)
ys = np.ones(len(xs), pycc.PointCoordinateType) * 18.5
zs = np.ones(len(xs), pycc.PointCoordinateType) * 17.33
pc = pycc.ccPointCloud(xs, ys, zs)
# Create and add a new scalar field
idx = pc.addScalarField("Intensity")
scalar_field = pc.getScalarField(idx)
# Change the values
sfArray = pc.getScalarField(idx).asArray()
num_chunk = 4
chunk_size = len(xs) // num_chunk
for i in range(num_chunk):
sfArray[i*chunk_size:(i+1)*chunk_size] = i
# Make it so the scalar field is displayed
scalar_field.computeMinAndMax()
pc.setCurrentDisplayedScalarField(idx)
pc.showSF(True)
# Optional: choose the color scale
scale = pycc.ccColorScalesManager.GetDefaultScale(
pycc.ccColorScalesManager.YELLOW_BROWN
)
scalar_field.setColorScale(scale)
pc.setName("Scalar Field Example")
# Change the point size so that the cloud is more
# visible by default (only needed here as the point cloud
# is very small)
pc.setPointSize(7)
pycc.GetInstance().addToDB(pc)
Polyline
import pycc
import cccorelib
import numpy as np
CC = pycc.GetInstance()
VERTICES = np.array([
[-0.5, -0.5, 0],
[1, 1, 0],
[2, 2, 0]
])
vertices = pycc.ccPointCloud(VERTICES[:, 0], VERTICES[:, 1], VERTICES[:, 2])
polyline = pycc.ccPolyline(vertices)
polyline.setColor(pycc.Rgb(255, 0, 0)) # set the color to red
polyline.showColors(True)
polyline.setClosed(False)
# This is important, otherwise the polyline would have a size of 0
polyline.addPointIndex(0, 3)
CC.addToDB(polyline)
CC.updateUI()
polyline.getDisplay().display3DLabel("Hello, world", cccorelib.CCVector3(1, 1,0))
Sub sampling
import cccorelib
import pycc
CC = pycc.GetInstance()
def doSubSampling(pc):
refcloud = cccorelib.CloudSamplingTools.subsampleCloudRandomly(pc, pc.size() // 2)
randomPc = pc.partialClone(refcloud)
randomPc.setName("Randomly subsampled")
CC.addToDB(randomPc)
refcloud = cccorelib.CloudSamplingTools.subsampleCloudWithOctree(pc, pc.size() // 4,
cccorelib.CloudSamplingTools.RANDOM_POINT)
randomPc = pc.partialClone(refcloud)
randomPc.setName("Subsampled using octree (RANDOM_POINT)")
CC.addToDB(randomPc)
refcloud = cccorelib.CloudSamplingTools.subsampleCloudWithOctree(pc, pc.size() // 4,
cccorelib.CloudSamplingTools.NEAREST_POINT_TO_CELL_CENTER)
randomPc = pc.partialClone(refcloud)
randomPc.setName("Subsampled using octree (NEAREST_POINT_TO_CELL_CENTER)")
CC.addToDB(randomPc)
def main():
entities = CC.getSelectedEntities()
print(f"Selected entities: {entities}")
if not entities:
raise RuntimeError("No entities selected")
pointCloud = entities[0]
print(pointCloud)
pycc.RunInThread(doSubSampling, pointCloud)
CC.updateUI()
if __name__ == '__main__':
main()
Merge
import pycc
def merge(clouds):
total_num_points = sum(cloud.size() for cloud in clouds)
merge_result = pycc.ccPointCloud("MergeResult")
merge_result.reserve(total_num_points)
for cloud_idx, cloud in enumerate(clouds):
for point_idx in range(cloud.size()):
merge_result.addPoint(cloud.getPoint(point_idx))
pos = 0
for cloud in clouds:
for i in range(cloud.getNumberOfScalarFields()):
scalarFieldName = cloud.getScalarFieldName(i)
idx = merge_result.getScalarFieldIndexByName(scalarFieldName)
if idx == -1:
idx = merge_result.addScalarField(scalarFieldName)
if idx == -1:
raise RuntimeError("Failed to add ScalarField")
scalarField = cloud.getScalarField(i)
sf = merge_result.getScalarField(idx)
sf.asArray()[pos: pos + scalarField.size()] = scalarField.asArray()[:]
sf.computeMinAndMax()
pos += cloud.size()
return merge_result
def main():
CC = pycc.GetInstance()
clouds = CC.getSelectedEntities()
merged_cloud = merge(clouds)
CC.addToDB(merged_cloud)
if __name__ == '__main__':
main()
Crop2D
import pycc
import cccorelib
CC = pycc.GetInstance()
pc_to_crop = CC.getSelectedEntities()[0]
bbMin, bbMax = cccorelib.CCVector3(), cccorelib.CCVector3()
pc_to_crop.getBoundingBox(bbMin, bbMax)
print(f"Min {bbMin}, Max: {bbMax}")
diag = bbMax - bbMin
bbMin = bbMin + cccorelib.CCVector3(diag.x / 2, 0, 0)
vertices = pycc.ccPointCloud()
vertices.setName("polyline.vertices")
vertices.addPoint(bbMin)
vertices.addPoint(bbMin + cccorelib.CCVector3(0, diag.y / 2, 0))
vertices.addPoint(bbMin + cccorelib.CCVector3(diag.x / 2, diag.y / 2, 0))
vertices.addPoint(bbMin + cccorelib.CCVector3(diag.x / 2, 0, 0))
polyline = pycc.ccPolyline(vertices)
polyline.setClosed(True)
# This is important, otherwise the polyline would have a size of 0
polyline.addPointIndex(0, vertices.size())
# To see the crop area
# CC.addToDB(polyline)
cropped_ref = pc_to_crop.crop2D(polyline, 2, True)
if cropped_ref is None:
raise RuntimeError("Failed to crop")
if cropped_ref.size() != 0:
cropped = pc_to_crop.partialClone(cropped_ref)
CC.addToDB(cropped)
else:
print("No points fall in crop area")
Using OpenGL Transformations
Translation
"""
This scripts applies a translation of the first selected entity.
It's an OpenGL translation, so coordinates are not changed, its just visual
"""
import pycc
import cccorelib
import math
entity = pycc.GetInstance().getSelectedEntities()[0]
# Translating the entity
glMat = entity.getGLTransformation()
translation = glMat.getTranslationAsVec3D()
translation.x += 10.0
glMat.setTranslation(translation)
entity.setGLTransformation(glMat)
entity.applyGLTransformation_recursive()
pycc.GetInstance().redrawAll()
Rotation
"""
This scripts applies a rotation of the first selected entity.
It's an OpenGL rotation, so coordinates are not changed, its just visual
"""
import pycc
import cccorelib
import math
entity = pycc.GetInstance().getSelectedEntities()[0]
# Rotating the entity around its center
# Rotation always happen in OpenGL`s origin which is probably not
# the center of our object
#
# So we have to:
# 1 - Translate the matrix by -center of the object
# 2 - Rotate the matrix by the desired amount
# 3 - Translate the matrix by centre of the object
center = entity.getDisplayBB_recursive(True).getCenter()
# 1
glTrans = entity.getGLTransformation()
translation = glTrans.getTranslationAsVec3D()
translation = translation - center
glTrans.setTranslation(translation)
entity.setGLTransformation(glTrans)
entity.applyGLTransformation_recursive()
# 2 & 3
glRot = pycc.ccGLMatrix()
glRot.initFromParameters(
math.radians(45),
cccorelib.CCVector3(0.0, 0.0, 1.0),
cccorelib.CCVector3(0.0, 0.0, 0.0)
)
glMat = entity.getGLTransformation()
glMat = glMat * glRot;
translation = glMat.getTranslationAsVec3D()
translation = translation + center
glMat.setTranslation(translation)
entity.setGLTransformation(glMat)
entity.applyGLTransformation_recursive()
pycc.GetInstance().redrawAll()