alternating_flagstones
pleat.alternating_flagstones ¶
Alternating-flagstone origami crease patterns.
This module is the high-level pipeline around
:func:pleat.conway.alternating_flagstone_graph. The Conway operator alone
produces a topological crease pattern: it does not know that, for the
result to fold flat, every flagstone must be a similar (rotated, uniformly
scaled) copy of the original face, and that the crease lengths around each
star vertex must match.
The pipeline implemented here is:
- :func:
build_structureappliesalternating_flagstone_graphto a tilingGand walks the resulting graph to identify the per-face "flagstones", the per-vertex "stars", and the radial / diagonal creases that connect them. Initial mountain/valley assignments are also placed here. - :func:
optimize_alternating_flagstoneadjusts a single globalscale, a per-flagstone rotation and 2D offset, and a 2D position for every star, so that the diagonal-crease lengths and star-arm lengths satisfy the foldability constraints. This step requires PyTorch (the optionaltorchextra); the rest of the module does not. - :func:
connection_length_metricreports how close the optimised CP is to a foldable solution. - :func:
extend_bordermirrors the outermost flagstone vertices across each border edge to produce a CP whose boundary closes properly. - :func:
cut_twist_centresremoves a small polygon around every star vertex, producing a CP suitable for physical folding (or for :func:pleat.overlap.fold_wireframe). - :func:
subdivide_ridges_for_curved_foldsubdivides each ridge crease so that, when folded, it traces an approximate circular arc — the input format expected by https://origamisimulator.org/.
AlternatingFlagstoneStructure
dataclass
¶
AlternatingFlagstoneStructure(
original: "EuclideanPositionHEG",
CP: "EuclideanPositionHEG",
flagstone_faces: list = list(),
original_faces: list = list(),
star_vertices: list = list(),
flagstone_corners: list = list(),
f_to_flagstone: dict = dict(),
v_to_star: dict = dict(),
corner_to_original: dict = dict(),
original_to_corner: dict = dict(),
star_groups: dict = dict(),
)
Bookkeeping for an alternating-flagstone CP.
Attributes:
| Name | Type | Description |
|---|---|---|
original |
'EuclideanPositionHEG'
|
The input tiling (a copy of the user's graph; mutating it will not affect the user-supplied original). |
CP |
'EuclideanPositionHEG'
|
The crease pattern produced by
:func: |
flagstone_faces |
list
|
The faces of CP that came from a face of original,
in the same order as |
original_faces |
list
|
For each entry of |
star_vertices |
list
|
The vertices of CP that came from a vertex of original. |
flagstone_corners |
list
|
The "free" flagstone-corner vertices of CP (one per (face, vertex) pair of original). |
f_to_flagstone |
dict
|
Maps a face of original to its flagstone face in CP. |
v_to_star |
dict
|
Maps a vertex of original to its star vertex in CP. |
corner_to_original |
dict
|
Maps |
original_to_corner |
dict
|
The inverse of |
star_groups |
dict
|
For each star vertex, the set of flagstone-corner vertices that should be equidistant from it. |
build_structure ¶
build_structure(
G: "EuclideanPositionHEG",
t: float = 0.5,
*,
assign_initial_creases: bool = True
) -> AlternatingFlagstoneStructure
Apply the alternating-flagstone Conway operator and build lookup tables.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
G
|
'EuclideanPositionHEG'
|
The input tiling. Will be copied; the user's graph is not mutated. |
required |
t
|
float
|
Parameter forwarded to
:func: |
0.5
|
assign_initial_creases
|
bool
|
If True, set every flagstone-perimeter
half-edge to MOUNTAIN and every other half-edge to VALLEY,
then call :func: |
True
|
Source code in pleat/alternating_flagstones.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | |
connection_length_metric ¶
Quantify how close the CP is to a foldable alternating-flagstone solution.
Returns a dict with keys:
max_relative_length_error: max relative discrepancy between a diagonal-crease length and the corresponding flagstone-side length.max_angle_error_rad/max_angle_error_deg: max mismatch between the angle of the original-tiling edge and the angle of the matching ridge crease.summary: a human-readable, multi-line string suitable for printing or for theextra_infoargument of :func:pleat.overlap.save_results.
Source code in pleat/alternating_flagstones.py
optimize_alternating_flagstone ¶
optimize_alternating_flagstone(
structure: AlternatingFlagstoneStructure,
*,
n_steps: int = 10000,
initial_scale: float = 0.4,
lr_offset: float = 0.001,
lr_angle: float = 0.01,
lr_star: float = 0.01,
ramp_step: int = 150,
ramp_factor: float = 10.0,
momentum_after_ramp: float = 0.9,
tol: float = 1e-09,
progress: bool = False,
show_every: int | None = None
) -> list[float]
Optimise flagstone rotations / offsets / star positions for foldability.
Variables (all per-flagstone, plus per-star):
scale(scalar, shared): uniform shrink factor of every flagstone.angle_i(scalar, per flagstone): rotation around the original face midpoint.offset_i(2-vector, per flagstone): post-rotation translation.star_j(2-vector, per star vertex): position of the star.
Loss = (sum of squared errors of diagonal-crease lengths) + (sum of squared deviations of star-arm lengths from their mean).
Mutates structure.CP in place. Returns the per-step training loss
(useful for plotting / convergence diagnostics).
Raises:
| Type | Description |
|---|---|
ModuleNotFoundError
|
if PyTorch is not installed. |
Source code in pleat/alternating_flagstones.py
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | |
extend_border ¶
Extend the boundary of an alternating-flagstone CP by mirror reflection.
For every border edge whose adjacent ridge crease is not a mountain, the border edge is promoted to a mountain crease and a small triangle is added outside, with its apex placed by mirror-reflecting the inward-pointing ridge across the border edge. This is a cosmetic finishing pass that gives the CP a clean outer outline whose folded model closes properly.
The input graph is not modified; a new graph is returned.
.. note:: The algorithm is geometrically simple but topologically fragile: it walks a snapshot of the original border, and a previous triangle insertion can absorb a later border edge into an interior face. Such edges are silently skipped. As a result the function is best-effort — for many tilings (notably triangular bases) it processes every border edge cleanly, but on others (square / hex bases) some edges remain unextended.
Source code in pleat/alternating_flagstones.py
cut_twist_centres ¶
cut_twist_centres(
structure: AlternatingFlagstoneStructure,
*,
inset: float = -0.05
) -> "EuclideanPositionHEG"
Return a copy of structure.CP with the centre of every "twist" cut out.
For each star vertex, a small polygon enclosing the star and the
incident diagonal creases is removed via
:func:pleat.cutting.cut_out_poly. The remaining graph contains
only the foldable creases (mountain on the flagstone perimeter,
valley on the radial creases connecting flagstones to the now-missing
star).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
structure
|
AlternatingFlagstoneStructure
|
A structure produced by :func: |
required |
inset
|
float
|
Negative for outward inset (the default; matches the
notebook). Forwarded to :func: |
-0.05
|
Source code in pleat/alternating_flagstones.py
subdivide_ridges_for_curved_fold ¶
subdivide_ridges_for_curved_fold(
CP: "EuclideanPositionHEG", n_subdivisions: int = 15
) -> "EuclideanPositionHEG"
Subdivide ridge creases so they trace circular arcs when folded.
A ridge is a half-edge whose two adjacent faces are triangles whose
apex vertices are both star vertices (i.e. carry a pre_conway
attribute pointing to a vertex of the original tiling). Each ridge is
replaced by n_subdivisions short edges, with subdivision points
chosen so that, viewed from the apex, the polyline subtends equal
angle steps — this yields a smooth circular arc when folded, suitable
for import into Origami Simulator (https://origamisimulator.org/).
The input graph is not modified; a new graph is returned.