Project Point Cloud to Plane Defined by Point Normal
SUMMARY
Project Point Cloud to Plane Defined by Point Normal orthogonally projects every point of a 3D point cloud onto a plane defined by a point and a normal vector.
Each point is moved to its closest point on the plane that passes through point with normal plane_normal. This is equivalent to project_point_cloud_to_plane — the same orthogonal projection, just specifying the plane geometrically (a point + normal) instead of via [a, b, c, d] coefficients.
Use this Skill when you want to flatten a point cloud onto a plane you already have as a point and normal vector, e.g. from a plane-fitting or centroid/normal estimation step.
The Skill
from telekinesis import vitreous
projected_point_cloud = vitreous.project_point_cloud_to_plane_defined_by_point_normal(
point_cloud=point_cloud,
point=[0.0, 0.0, 0.0],
plane_normal=[0.0, 0.0, 1.0],
add_white_noise=False,
white_noise_standard_deviation=0.0,
)Data Transfer Notice
There is no longer a fixed limit of 1 million points per request. However, very large datasets may result in slower data transfer and processing times. We are continuously optimizing performance as part of our beta program, with ongoing improvements to enhance speed and reliability.
Example
Raw Pointcloud
Unprocessed point cloud. Plane for projection visualized in green.
Projected Pointcloud
Flattened pointcloud.
The Code
"""
Demonstrates projecting a point cloud onto a plane defined by a point and normal.
"""
from loguru import logger
import rerun as rr
from telekinesis import vitreous, datatypes
def project_point_cloud_to_plane_defined_by_point_normal_example():
"""
Projects points onto a plane defined by a point and normal (alternative parameterization).
Same as plane projection but using point+normal instead of coefficients.
"""
# ===================== Load Data ==========================================
point_cloud_url = (
"https://assets.telekinesis.ai/examples/v1/point_clouds/engine_parts_0.ply"
)
point_cloud = datatypes.PointCloud.from_url(url=point_cloud_url, use_cache=True)
# ===================== Run Skill ==========================================
projected_point_cloud = (
vitreous.project_point_cloud_to_plane_defined_by_point_normal(
add_white_noise=False,
white_noise_standard_deviation=1e-6,
point_cloud=point_cloud,
point=[0.0, 0.0, 0.0],
plane_normal=[0.0, 0.0, 1.0],
)
)
# ===================== Log ================================================
logger.success(f"Projected {point_cloud} to plane defined by point and normal")
logger.success(f"Results: {projected_point_cloud}")
logger.info(
f"Projected point cloud positions shape: {projected_point_cloud.positions.shape}"
)
logger.info(
f"Projected point cloud has normals shape: "
f"{projected_point_cloud.normals.shape if projected_point_cloud.has_normals else None}"
)
logger.info(
f"Projected point cloud has colors shape: "
f"{projected_point_cloud.colors.shape if projected_point_cloud.has_colors else None}"
)
# ===================== Visualization (Optional) ===========================
rr.init("project_point_cloud_to_plane_defined_by_point_normal_example", spawn=True)
datatypes.visualize(point_cloud, entity_path="/1-input_point_cloud")
datatypes.visualize(projected_point_cloud, entity_path="/2-filtered_point_cloud")
if __name__ == "__main__":
project_point_cloud_to_plane_defined_by_point_normal_example()Runnable examples are available in the Telekinesis examples repository.
Follow the README in that repository to set up the environment, run this specific example with:
cd telekinesis-examples
python examples/point_cloud/project_point_cloud_to_plane_defined_by_point_normal.pyParameter Configuration
These parameters are passed directly to the plane-projection service and control which plane (given as a point + normal) the cloud is projected onto, and whether synthetic noise is added to the result.
| Key | Type | Default | Description |
|---|---|---|---|
point_cloud | datatypes.PointCloud | required | The point cloud to project |
point | datatypes.Vector3D | np.ndarray | list[float] | required | Any 3D point [x, y, z], in meters, that lies on the plane — defines the plane's position in space |
plane_normal | datatypes.Vector3D | np.ndarray | list[float] | required | The plane's normal direction [x, y, z], perpendicular to its surface — should be a unit vector. Defines the plane's orientation |
add_white_noise | datatypes.Bool | bool | False | Whether to add random Gaussian noise to the projected points, e.g. to simulate measurement uncertainty when generating synthetic test data |
white_noise_standard_deviation | datatypes.Float | float | int | 0.0 | Standard deviation, in meters, of the Gaussian noise added when add_white_noise is True; ignored otherwise |
Returns
| Type | Description |
|---|---|
datatypes.PointCloud | A point cloud with every point moved onto the plane. Use .positions for the projected (N, 3) position array. |
Raises
| Exception | Condition |
|---|---|
TypeError | A parameter's value does not match its expected type (see the Parameter Configuration table above) |
ValueError | point or plane_normal does not have exactly 3 elements |
ConfigurationError | The TELEKINESIS_API_KEY environment variable is not set |
SerializationError | The request input failed to serialize, or the response failed to deserialize |
RequestTimeoutError | The request to the Vitreous service timed out |
TransportError | A network failure occurred before a response was received |
ClientError | The Vitreous service rejected the request due to invalid input, invalid data, or another unexpected 4xx response |
AuthenticationError | The API key was rejected as invalid or expired |
AuthenticationServiceError | The authentication service was unavailable |
ServerError | The Vitreous service returned a 5xx or otherwise unexpected error response |
How to Tune the Parameters
point
- Controls: A point that lies on the target plane, fixing the plane's position in space.
- Units: Meters
- Default: required, no default
- Any point on the plane works — a natural choice is the centroid of a segmented planar region (e.g. via
calculate_point_cloud_centroid)
plane_normal
- Controls: The plane's orientation — the direction perpendicular to its surface.
- Units: Dimensionless (unit vector)
- Default: required, no default
- Normalize to unit length before passing it in; an un-normalized vector still points the right direction but is not a valid unit normal
- Often obtained from plane segmentation/estimation (e.g. the
[a, b, c]part ofsegment_point_cloud_using_plane's output)
add_white_noise
- Controls: Whether Gaussian noise is added to the projected points after they are moved onto the plane.
- Units: Boolean
- Default:
False - Keep
Falsefor exact, deterministic projection - Set
Trueonly to synthesize noisy test data
white_noise_standard_deviation
- Controls: The spread of the Gaussian noise added when
add_white_noise=True. Ignored whenadd_white_noise=False. - Units: Meters
- Default:
0.0 - Increase → more variation around the projected plane position
- Decrease → projected points stay closer to the exact plane
- Typical range: 0.0–0.01 meters — use 0.001–0.005 for small variation, 0.005–0.01 for larger variation
TIP
Keep add_white_noise=False for exact projection. Make sure plane_normal is normalized, and pick a point that actually lies on the intended plane — e.g. from calculate_point_cloud_centroid on a segmented planar region.
Where to Use the Skill
Common pipelines include:
- Planar surface alignment – flattening scanned parts onto a known reference plane before inspection
- Ground/wall plane flattening – reducing floor or wall scans to a true 2D-in-3D representation for mobile robot mapping
- 2D feature extraction – standardizing near-planar surfaces for downstream shape or area analysis
- Registration preparation – aligning point clouds onto a common plane before registration or segmentation
Alternative Skills
| Skill | vs. Project Point Cloud to Plane (Point + Normal) |
|---|---|
| project_point_cloud_to_plane | Projects onto the exact same kind of plane, just specified by [a, b, c, d] coefficients instead of a point + normal. Use whichever parameterization you already have on hand — the two are otherwise equivalent. |
When Not to Use the Skill
Do not use Project Point Cloud to Plane (Point + Normal) when:
- You have
[a, b, c, d]plane coefficients instead — useproject_point_cloud_to_planedirectly rather than converting - You need to preserve full 3D structure — projection flattens the cloud onto the plane, discarding out-of-plane detail
- You need a non-orthogonal projection — this Skill only performs orthogonal (closest-point) projection
pointdoesn't actually lie on the intended plane, orplane_normalisn't normalized — the projection will still run, but onto a different plane than you intended
TIP
If you don't have a point and normal yet, segment the plane first (segment_point_cloud_using_plane), then derive a point (e.g. calculate_point_cloud_centroid on the segmented points) and normal from that result.

