Concept units

Shapes as point clouds

Intermediate

Read these first if they are new to you

The idea in one paragraph

A point cloud describes a shape by a list of points on it or inside it. Each point is three numbers, its x, y and z coordinates. The list has no special order: shuffle it and you still have the same shape. The number of points is a choice. More points show more detail and cost more memory. A point cloud stores only where the shape is, so it stays small even for a long, thin object in a big space.

A point cloud is a table of points, one row per point

Picture a donut dusted with flour. Each grain of flour sits at one spot on the surface. If you write down where every grain sits, you can draw the donut again from that list alone. That list is a point cloud: a set of points that together show a shape.

Each point is a position in space, written as three numbers (see vectors). So a point cloud with N points is a table with N rows and 3 columns. In Python this table is an array of shape (N, 3) (see arrays in NumPy).

Before you use the viewer below, guess: how many points do you need before you can tell that the shape is a donut?

The same donut drawn with more or fewer points
Goal: drag "Points" up from 60 until you are sure the shape is a donut, then turn it. Each dot is one row of the table. Nothing joins the dots; your eye fills in the surface. You can also drag the picture to turn it.

With a few hundred points the hole becomes clear. With a few thousand the surface looks smooth. The shape did not change. Only the number of samples did.

The order of the rows does not matter

The table lists the points in some order, but the order carries no meaning. Swap two rows and the shape stays the same. So a point cloud is really a set: a collection where only membership counts, not position in a list.

Two numberings of the same five points draw the same shape
Order A 123 45 Order B (shuffled) 415 32
What to notice. The numbers next to the points differ, but the points sit in the same places. The dashed outline is only a guide for your eye; a point cloud does not store it.

This fact shapes every program that reads point clouds. A program should give the same answer for both numberings above. A function with that property is called permutation invariant; the unit order doesn't matter explains how a model can have it.

A point cloud stores only where the shape is

Another way to store a shape is a 3D grid of tiny boxes that are each full or empty (see voxels). A grid spends memory on every box, including all the empty space around the shape. A point cloud spends memory only on the shape itself.

Worked example: the memory of a point cloud and of a grid

A program stores each coordinate as a 32-bit decimal number, which takes 4 bytes.

  1. A point cloud of 2,048 points has 2,048 × 3 = 6,144 numbers.
  2. That takes 6,144 × 4 = 24,576 bytes, about 25 kilobytes.
  3. A grid of 256 × 256 × 256 boxes, at 1 byte per box, takes 16,777,216 bytes, about 17 megabytes.
  4. The grid is 16,777,216 ÷ 24,576 ≈ 683 times larger.

The gap grows for thin shapes, such as a long wire, because most of the grid is then empty.

The price is that a point cloud has a fixed level of detail. Between two points, it says nothing. If a part of the shape is thinner than the gap between points, that part can vanish. Programs often choose a fixed number of points per shape, spread evenly; farthest-point sampling is one way to choose them.

This is a simplification

Here each point holds only a position. Real point clouds often carry more numbers per row, such as a color, a brightness, or the direction the surface faces at that point (its normal). Also, some point clouds sample only the surface of a shape, while others fill its inside. Which one you have depends on how the points were made.

In NumPy, a point cloud is an array with three columns

This snippet builds the eight corners of a cube as a point cloud, shuffles the rows, and checks that the shape is unchanged.

point_clouds.py

import numpy as np

# The 8 corners of a cube with side 2, centered at the origin.
# Each corner takes x, y and z from (-1, 1); dtype=float stores them as decimals.
cloud = np.array([[x, y, z] for x in (-1, 1) for y in (-1, 1) for z in (-1, 1)], dtype=float)
print(cloud.shape)

rng = np.random.default_rng(0)
shuffled = cloud[rng.permutation(len(cloud))]   # the same points, rows in a new order
print(cloud[0], shuffled[0])                     # the first rows differ
print(cloud.mean(axis=0), shuffled.mean(axis=0)) # the center does not
# np.array_equal: do the two sorted lists of rows match?
print(np.array_equal(np.unique(cloud, axis=0), np.unique(shuffled, axis=0)))
(8, 3)
[-1. -1. -1.] [-1.  1. -1.]
[0. 0. 0.] [0. 0. 0.]
True

The center, the mean of all the points, does not depend on the order. Neither does the sorted list of rows that np.unique returns. Both are examples of permutation-invariant summaries.

Check yourself

  1. A point cloud has 4,096 points with only x, y, z per point. What is the shape of its NumPy array, and how many bytes does it take at 4 bytes per number?

    Answer

    The shape is (4096, 3). It holds 4,096 × 3 = 12,288 numbers, so it takes 12,288 × 4 = 49,152 bytes.

  2. A program reads a point cloud and reports the coordinates of its first row as "the tip of the shape." What is wrong with that?

    Answer

    The first row is not special. Shuffling the rows gives the same shape but a different first row, so the program would report a different "tip." A useful answer must not depend on the order of the rows.

  3. You sample a thin wire, 1 unit long, with points spaced about 0.1 units apart. The wire has a gap of 0.02 units in the middle. Will the point cloud show the gap?

    Answer

    Probably not. The gap is much smaller than the spacing between points, so the two sides look continuous. To see the gap, you need points spaced closer than 0.02 units near it.

Go further

Optional background. This unit covers what you need here.

Where this shows up

Units that build on this one