Origami RL

environment

OpenEnv RL environment for origami folding. Submit FOLD crease patterns, get physics simulation + shape similarity reward.

Rewards inspired by AlphaFold — chamfer distance shape matching with rotational alignment across 24 orientations.

How It Works

1
Reset
Pick a task → get target shape and flat paper starting state
2
Step
Submit a FOLD JSON crease pattern describing your fold
3
Reward
shape_similarity × 20.0 — score from 0 to 20

FOLD Format

vertices_coords [[x,y], ...] Vertex positionsrequired
edges_vertices [[v1,v2], ...] Edge connectivityrequired
edges_assignment ["B"|"M"|"V"|"F"|"U"] Edge types. Need ≥1 M or V, ≥1 Brequired
edges_foldAngle [degrees, ...] Fold angles. Defaults: V→180°, M→−180°optional
faces_vertices [[v0,v1,...], ...] Face polygons. Auto-computed if missingoptional

Observation

shape_similarity float 0.0–1.0 Procrustes match to target shape
final_positions [[x,y,z], ...] Folded vertex positions
target_positions [[x,y,z], ...] Expected target positions
max_strain float Edge deformation metric
is_stable bool Convergence flag
reward float similarity × 20.0, or −2.0 on error

Rewards

The environment computes reward from the physics simulation result. Two reward functions are available for training:

valid_fold format reward +1.0 valid FOLD JSON, −0.5 parseable but invalid structure, −2.0 not parseable
shape_match main reward similarity × 20.0 (0–20). −1.0 if simulation fails, −2.0 if invalid

How shape_similarity is computed

1. Simulate Run physics engine on submitted crease pattern → get final 3D vertex positions
2. Center Center both predicted and target point clouds at origin
3. Align Try 24 rotation alignments (90° rotations + mirrors) to handle equivalent orientations
4. Chamfer Bidirectional nearest-neighbor distance, normalized by bounding box diagonal
5. Score similarity = 1 − (chamfer / diagonal), clamped to [0, 1]. Reward = similarity × 20

max_strain measures edge length deviation after folding (0 = no deformation). is_stable indicates whether the simulation converged.

API Reference

WebSocket

WS /ws Persistent connection
Send: Reset
{"type": "reset", "data": {"task_name": "triangle"}}
Send: Step
{"type": "step", "data": {"fold_data": {...}}}
Receive: Observation
{"type": "observation", "data": {"reward": 20.0, "done": true, ...}}

REST

POST /sessions Create session
POST /sessions/{id}/reset Reset with task_name
POST /sessions/{id}/step Submit fold action
GET /tasks List all tasks
GET /tasks/{name} Task detail + target fold

Quick Start

python
from origami_env.client import OrigamiEnv
from origami_env.models import OrigamiAction

with OrigamiEnv(base_url="http://localhost:8000") as env:
    env.reset(task_name="triangle")
    result = env.step(OrigamiAction(fold_data={
        "vertices_coords": [[0,0],[1,0],[1,1],[0,1]],
        "edges_vertices": [[0,1],[1,2],[2,3],[3,0],[0,2]],
        "edges_assignment": ["B","B","B","B","V"],
        "edges_foldAngle": [0,0,0,0,180]
    }))
    print(result.observation.shape_similarity)  # 1.0
python — all tasks
import requests

# Fetch available tasks
tasks = requests.get("http://localhost:8000/tasks").json()

for name, info in tasks.items():
    print(f"{name}: difficulty {info['difficulty']}")

    # Get target crease pattern for this task
    detail = requests.get(f"http://localhost:8000/tasks/{name}").json()
    target = detail["target_fold"]

    # Create session, reset, step
    s = requests.post("http://localhost:8000/sessions").json()
    sid = s["session_id"]
    requests.post(f"http://localhost:8000/sessions/{sid}/reset", json={"task_name": name})
    obs = requests.post(f"http://localhost:8000/sessions/{sid}/step", json={"fold_data": target}).json()
    print(f"  reward: {obs['reward']}, similarity: {obs['shape_similarity']}")
Crease Pattern
Mountain
Valley
Boundary
flat
100%
folded
Similarity
Folds
Strain
Vertices
Status