detect_reflection_symmetry — 3D symmetry op

• Data kinds: points → primitive

• Call: import fullseye as fs; fs.ledger.detect_reflection_symmetry(points) (to call the implementation directly, import symmetry3d; symmetry3d.detect_reflection_symmetry(points); from the registry, ops3d.get("detect_reflection_symmetry"))

Usage

Choose the best mirror-symmetry plane among candidates whose normals are the PCA principal axes (through the centroid).

→ dict{plane_point, plane_normal, score, all_scores, margin}. The smaller the score, the more symmetric.

★ Do not decide acceptance from score alone. Look at margin (added 2026-09-06). margin is the difference between the 2nd- and 1st-place scores: if the candidates are bunched, the answer is a lottery is the warning it gives. Measured (1500 points):

======================= ======== ======== =========

Shape best 2nd margin

======================= ======== ======== =========

Box (3 mirror planes) 0.9397 0.9499 0.0102

Box with a prism 0.9876 0.9918 0.0043

Sphere 1.0490 1.0551 0.0062

Uniform random cube 1.0608 1.3435 0.2828

======================= ======== ======== =========

The more symmetry planes a shape has, the smaller the margin; this is not a bug but information that the shape is like that. A point-cloud registration PoC measures the same structure: the PCA candidates grab a flipped quadrant 83 % of the time on a plain cuboid, and what collapses first then is not the residual but the margin between candidates (0.42 → 0.066 → 0.039). Judge acceptance by margin, not by the residual is the conclusion common to both.

★ score does not approach 0. Even for an exactly symmetric shape, the point spacing creates a floor (the box is at 0.94 in the table above). Instead of asking "is it close to 0", compare with "the value measured on the same shape without reflecting it" or with "the point spacing".

★ The candidates are only the 3 PCA axes. For a shape whose true symmetry plane matches none of the principal axes, the plane is not found, and the op silently returns the best of the 3 anyway. If you need densely sampled candidates, sweep reflection_symmetry_score directly.

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

Runnable examples (verified samples that actually call this op)

• dl_mesh_symmetry — py -3.11 examples_3d/dl_mesh_symmetry.py

• itokawa_symmetry_honest — py -3.11 examples_3d/itokawa_symmetry_honest.py

• reflection_symmetry — py -3.11 examples_3d/reflection_symmetry.py

• symmetry — py -3.11 examples_3d/symmetry.py

Ops the type connects to (they accept primitive as input)

fuse_to_voxel · angle_between_lines · angle_between_planes · angle_line_plane · distance_point_plane · distance_point_line · distance_line_line · distance_segment_segment

Same category (symmetry)

detect_rotational_symmetry · reflect_points · reflection_symmetry_score


*Provenance: symmetry3d.py — 3D operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.