Python implementation of the Grosman 2008 UZY positioning methodology for objective, reproducible orientation of 3D scanned lithic artifacts.
This repository provides a complete Python implementation of the UZY (Uzy Smilansky) positioning method for objectively orienting 3D lithic artifacts. The methodology uses:
- Eigenvector analysis of surface normal distribution (inertia tensor)
- Planform optimization (maximum projected area for face-on view)
- Mirror symmetry alignment (bilateral symmetry detection)
- Upright orientation (longest dimension vertical)
This eliminates researcher subjectivity in artifact measurement. The original paper demonstrated 18-28% measurement variation depending on manual positioning choices.
UV is a fast Python package manager.
# Setup environment
./setup-env.sh # macOS/Linux
# or
setup-env.bat # Windows
# Activate
source .venv/bin/activatepython -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate.bat
pip install -r requirements.txt# Process single file (default: save as <input>_positioned.glb)
python positioning.py artifact.glb
# Specify output file
python positioning.py artifact.glb -o output.glb
# UZY positioning only (skip planform/symmetry)
python positioning.py artifact.glb --uzy-only
# Save transformation metadata to JSON
python positioning.py artifact.glb --save-metadata metadata.json
# Verbose output with statistics
python positioning.py artifact.glb --verbose
# Open 3D viewer after processing
python positioning.py artifact.glb --show
# Quiet mode (errors only)
python positioning.py artifact.glb --quietimport trimesh
from positioning import positioning
# Load mesh
mesh = trimesh.load('artifact.glb')
# Apply complete positioning
v_positioned, metadata = positioning(
mesh.vertices,
mesh.faces,
include_planform=True,
include_mirror_symmetry=True
)
# Save result
mesh.vertices = v_positioned
mesh.export('artifact_positioned.glb')
# Check metadata
print(f"Planform rotation: {metadata['planform_rotation_index']}")
print(f"Symmetry angle: {metadata['symmetry_angle']}°")
print(f"Upright rotation: {metadata['upright_rotation']}")MeshModelRotate/
├── positioning.py # Main implementation (678 lines, production-ready)
├── test_positioning.py # Single-file test script
├── utilities/
│ ├── test_rotations.py # Rotation matrix verification
│ ├── glbutils.py # Mesh utilities
│ └── mesh2glb copy.py # Format converter
├── 3DModels/ # Sample GLB files (Wansunt collection)
├── MATLAB/ # Original MATLAB implementation (reference)
├── pyproject.toml # Python project configuration
├── requirements.txt # Pip dependencies
├── setup-env.sh / .bat # Environment setup scripts
├── CLAUDE.md # Development documentation
└── README.md # This file
The implementation follows the methodology described in:
Grosman, L., Smikt, O., & Smilansky, U. (2008). On the application of 3-D scanning technology for the documentation and typology of lithic artifacts. Journal of Archaeological Science, 35(12), 3101-3110.
-
Center to Origin
- Translates mesh bounding box center to (0, 0, 0)
-
UZY Positioning
- Computes eigenvectors of surface normal distribution (inertia tensor)
- Aligns artifact with principal axes
- Provides initial objective orientation
-
Planform Optimization
- Tests 3 orthogonal rotations (90° increments)
- Selects orientation with maximum XY projected area
- Ensures "face-on" view (lying flat)
-
Re-center to Origin
- Translates to origin after rotation
-
Mirror Symmetry Alignment
- Rotates 360° in 1° steps about Z-axis
- Finds angle minimizing distance between mirrored halves
- Ensures consistent left/right orientation
-
Final Upright Orientation
- Identifies longest dimension
- Rotates to make it vertical (Y-axis)
- Ensures artifact stands upright
- UZY positioning: ~instant
- Planform optimization: ~instant (3 rotations)
- Mirror symmetry: ~1-2 seconds (360° search)
- Total: ~2 seconds per artifact
- Right-handed coordinate system (glTF/GLB standard)
- X-axis: Width (left/right)
- Y-axis: Height (vertical, up/down)
- Z-axis: Depth (front/back)
Final output: face-on, symmetry-aligned, upright (longest dimension vertical)
Complete UZY positioning workflow.
Parameters:
vertices(np.ndarray): Nx3 array of vertex coordinatesfaces(np.ndarray): Mx3 array of triangle face indices (0-indexed)include_planform(bool): Enable planform optimization (default: True)include_mirror_symmetry(bool): Enable symmetry alignment (default: True)
Returns:
positioned_vertices(np.ndarray): Nx3 array of final positioned verticesmetadata(dict): Transformation informationcenter_offset: Original bounding box centeruzy_transform: 3x3 UZY transformation matrixplanform_rotation_index: Selected rotation (0=original, 1=90°X, 2=90°X+90°Y)planform_areas: List of 3 projected areas testedsymmetry_angle: Z-rotation angle in degrees (1-360)upright_rotation: Final rotation applied ('Z+90 (X→Y)', 'X+90 (Z→Y)', or 'none')
Example:
# Full positioning
v_positioned, meta = positioning(mesh.vertices, mesh.faces)
# UZY only (skip planform/symmetry)
v_uzy, meta = positioning(
mesh.vertices,
mesh.faces,
include_planform=False,
include_mirror_symmetry=False
)Convert mesh files to GLB format:
# Single file
python utilities/mesh2glb.py input.ply output.glb
# Batch conversion
python utilities/mesh2glb.py # Processes ToConvert/ directorySupported formats: PLY, WRL, STL, OBJ, OFF, GLB, GLTF
# Test rotation matrices
python utilities/test_rotations.py
# Export to MATLAB format
python utilities/save_mat_for_matlab.py artifact.glb- trimesh ≥4.0.0 - 3D mesh processing
- scipy ≥1.11.0 - ConvexHull for projected area
- numpy ≥1.24.0 - Array operations
- pymeshlab ≥2022.2 - Additional format support (WRL, etc.)
Install all:
pip install trimesh scipy numpy pymeshlab- Mirror symmetry uses brute-force 360° search (could be optimized with gradient descent)
- Input meshes should be manifold, watertight
- No validation for degenerate triangles or NaN vertices
- Face indices must be 0-indexed (Python/NumPy convention)
- Assumes right-handed coordinate system (glTF standard)
- No automatic detection/conversion of coordinate system conventions
The original MATLAB implementation is available in MATLAB/ for reference and validation purposes. The Python implementation is recommended for production use.
See MATLAB/README.md for details on the MATLAB scripts.
Grosman, L., Smikt, O., & Smilansky, U. (2008). On the application of 3-D scanning technology for the documentation and typology of lithic artifacts. Journal of Archaeological Science, 35(12), 3101-3110. doi:10.1016/j.jas.2008.06.011