Sketch on a cylinder primitive's caps; format 15 (#460)

* Sketch on a cylinder primitive's caps (#420)

A solid.cylinder's caps carried no construction certificate, so a sketch
on its top or bottom was refused unless fixed:true (#420's crossed
cylinders, built from primitives).

- kernel: FaceSource::CylinderCap { producer, high, plane };
  Body::with_cylinder_certificate turns a cylinder's two extrude caps into
  certified caps. kernel::cylinder's output is unchanged for its other
  callers.
- doc: FaceCertificate::CylinderCap { high }, named F<id>:top / F<id>:bottom
  as the cylinder's face names already are; New Body and Join cylinders
  are certified, shells carry it as their planar source.
- format 15: designs can store the new certificate kind; version 14
  designs need no change, and older builds ask for an update instead of
  meeting an unknown kind.

Tests: top and bottom attachments follow a height and base edit and a
reopen; crossed joined cylinders keep both caps attachable; a shelled
cylinder's floor keeps its cap identity; malformed certificate JSON is
refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDDPmNdRoBgkHcLeUDKVfb

* Format 15 also covers sketch_axis links (#448)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDDPmNdRoBgkHcLeUDKVfb

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Irakli KourtisandClaude Opus 5.5 authored and GitHub committed 2026-10-12 21:32:07 -04:00
1 parent b24e478bc1
commit 49956c2797
10 files changed
+247 -23

No files matched your search

+3 -1
View File
@@ -30,6 +30,7 @@ fn base_plane(source: &FaceSource, actual: &Plane) -> Option<Plane> {
FaceSource::CertifiedExtrudeSide { plane: Some(p), .. }
| FaceSource::CertifiedExtrudeCap { plane: p, .. }
| FaceSource::Box { producer: Some(_), plane: p, .. }
| FaceSource::CylinderCap { plane: p, .. }
| FaceSource::ShellPlanar { producer: Some(_), plane: p, .. } => Some(*p),
FaceSource::Imported { producer: Some(_), payload_sha256: Some(_), .. } => Some(*actual),
_ => None,
@@ -41,6 +42,7 @@ pub(super) fn certified_plane(source: &FaceSource) -> Option<Plane> {
FaceSource::CertifiedExtrudeSide { plane: Some(p), .. }
| FaceSource::CertifiedExtrudeCap { plane: p, .. }
| FaceSource::Box { producer: Some(_), plane: p, .. }
| FaceSource::CylinderCap { plane: p, .. }
| FaceSource::ShellPlanar { producer: Some(_), plane: p, .. } => Some(*p),
_ => None,
}
@@ -93,7 +95,7 @@ pub(super) fn descriptor_cost(source: &FaceSource) -> Option<usize> {
}
cost = cost.checked_add(count.checked_mul(128)?)?;
}
FaceSource::ExtrudeSide { .. } | FaceSource::Imported { .. } | FaceSource::Box { .. } => {}
FaceSource::ExtrudeSide { .. } | FaceSource::Imported { .. } | FaceSource::Box { .. } | FaceSource::CylinderCap { .. } => {}
_ => return None,
}
Some(cost)