# 5. operations on coordinates, scalar fields, colors, normals, meshes with Numpy

## 5.1. Read, modify or create cloud coordinates with Numpy

There are two ways to access the cloud coordinates from Numpy.

```
cloud = cc.loadPointCloud(getSampleCloud(5.0))

coords = cloud.toNpArrayCopy()
if coords.shape != (cloud.size(), 3):
raise RuntimeError
```

The above code snippet is from `test004.py`.

You can fill the cloud coordinates with `coordsFromNPArray_copy()`.

• The array should have a shape (numberOfPoints,3).

• WARNING Be sure to have an array data in C-style contiguous order. For instance, a transpose operation do not reorder the data in memory. Check the boolean array.flags `array.flags['C_CONTIGUOUS']` and, if `False`, reorder with `array.copy(order='C')`.

• The cloud memory is reserved /resized automatically.

## 5.2. Read, modify or create a scalar field with Numpy

There are two ways to access to the scalar field data from Numpy.

```
dic = cloud1.getScalarFieldDic()
sf1 = cloud1.getScalarField(dic['Coord. Z'])
max1 = sf1.getMax()
asf1 = sf1.toNpArray()   # access to Numpy array, without copy
asf1[0] = 2*max1         # modification in place
sf1.computeMinAndMax()
```

The above code snippet is from `test002.py`.

You can fill a cloud scalar field with `fromNpArrayCopy()`. The method checks if the scalar field and the array have the same size, before copy.

## 5.3. An example of point cloud with scalar field created from Numpy arrays

```
# --- generate a set of coords and a scalar field

npts = 10000000
phi = 2*np.pi*np.random.random((npts))
theta = 2*np.pi*np.random.random((npts))
r = 5 + 0.3*np.sin(2*2*np.pi*phi + 3*2*np.pi*theta)
x = np.float32(r*np.sin(phi)*np.cos(theta))
y = np.float32(r*np.sin(phi)*np.sin(theta))
z = np.float32(r*np.cos(phi))
coords = np.column_stack((x,y,z))
dr = np.float32(np.sqrt(x*x + y*y + z*z) -5)

# --- create the pointCloud, add the scalar field

cl = cc.ccPointCloud("boule")
cl.coordsFromNPArray_copy(coords)
cl.addScalarField("delta")
sf = cl.getScalarField(0)
sf.fromNpArrayCopy(dr)

# --- save the point cloud

res = cc.SavePointCloud(cl, os.path.join(dataDir, "boule.bin"))
```

The above code snippet is from `test017.py`.

## 5.4. Access to the indexes of triangles nodes in a mesh

In a mesh, the array of triangles indexes has a shape(number of triangles, 3), with indexes corresponding to the indexes of nodes in the associated cloud.

The method `IndexesToNpArray()` gives access to the array without copy. the corresponding nodes coordinates are given by the method `toNpArray()` from the associated cloud (`getAssociatedCloud()`. The method `IndexesToNpArray_copy()` creates a copy of the array of indexes.

```
# --- access to the numpy array of node indexes (one row per triangle)
d = mesh1.IndexesToNpArray()
if d.shape != (19602, 3):
raise RuntimeError
if d.dtype != np.dtype('uint32'):
raise RuntimeError

d2 = mesh1.IndexesToNpArray_copy()
if d2.shape != (19602, 3):
raise RuntimeError
if d2.dtype != np.dtype('uint32'):
raise RuntimeError
```

The above code snippet is from `test011.py`.

## 5.5. Access to the array of colors in cloud

The colors are stored in an array of shape(number of nodes, 4), the four components are R, G, B and alpha, each stored in a 8bits integer.

The method `colorsToNpArray()` gives access to the array of colors without copy, the method `colorsToNpArrayCopy()` creates a copy of the array.

```
cola = cloud.colorsToNpArray()
if not cola.shape == (1000000, 4):
raise RuntimeError
if not cola.dtype == 'uint8':
raise RuntimeError

colaCopy = cloud.colorsToNpArrayCopy()
if not colaCopy.shape == (1000000, 4):
raise RuntimeError
if not colaCopy.dtype == 'uint8':
raise RuntimeError
```

The above code snippet is from `test029.py`.

## 5.6. Read, modify or create normals with Numpy

Normals can be exported as Numpy arrays with `normalsToNpArrayCopy()`. As normals are stored compressed in the CloudCompare cloud, the method decompress the values in a new Numpy array (owned by Python). In the example below, we check that this is the same as converting normals into scalar fields and exporting these scalar fields to Numpy arrays.

```
cloud = cc.loadPointCloud(getSampleCloud(5.0))
cc.computeNormals([cloud])

cloud.exportNormalToSF(True, True, True)
dic = cloud.getScalarFieldDic()
sfx = cloud.getScalarField(dic['Nx'])
sfy = cloud.getScalarField(dic['Ny'])
sfz = cloud.getScalarField(dic['Nz'])
asfx = sfx.toNpArray()
asfy = sfy.toNpArray()
asfz = sfz.toNpArray()

normals=cloud.normalsToNpArrayCopy()

dx = normals[:,0] -asfx
dy = normals[:,1] -asfy
dz = normals[:,2] -asfz

if dx.max() != 0 or dx.min() !=0:
raise RuntimeError

if dy.max() != 0 or dy.min() !=0:
raise RuntimeError

if dz.max() != 0 or dz.min() !=0:
raise RuntimeError
```

It is also possible to import Numpy arrays as normals with `normalsFromNpArrayCopy()`. The numpy array must have the right type, shape and size. During import, the normals are normalized and compressed. In the following example, whe invert and denormalize the numpy array of normals before import/

```
normals *=-0.5 # an example of modification of the normals: invert and denormalize
cloud.normalsFromNpArrayCopy(normals)
```

The above code snippets are from `test054.py`.