Commit Graph
231 Commits
Author SHA1 Message Date
jyvikoandClaude Opus 5.5 5a4c501524 mesh: octree chooses its levels and root size before building
The thinnest wall (measured to a facing wall) and narrowest patch give the cell size the grid
needs; the requested size refined that deep comes first, a root shrunk so one level fewer is
exact second; beyond the levels or cells allowed meshing fails at once naming the wall, levels and
cells. max_depth is a cap (default 4); rebuilds stay within the planned levels; lattice positions
no longer depend on the lattice depth; the report gives root size, levels, wall, patch and cells
per level before and after closure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 15:40:28 -04:00
jyvikoandClaude Opus 5.5 54e367970f engine: geometry.face.split, a face split along a datum plane (#508)
geometry.face.split {name, face, datum} records a face split; it is
applied after the merges whenever the design is opened or cut again,
follows its datum (which it holds), undoes, and is saved with the
model. geometry.face.split.delete removes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 15:40:18 -04:00
jyvikoandClaude Opus 5.5 a2e747b9ba mesh: projection and relaxation keep to a merged face's B-rep pieces
A merged face on several surfaces (a sliver absorbed into its neighbour)
had no exact surface, so the projection target fell back to its
triangles: flat curvature probes gave fewer divisions round the boss and
the enclosure base's symmetric mesh dropped from 0.70 to 0.51 once its
absorbs reached the cells. The target now projects a point of such a
face onto the exact surface of the B-rep piece it is on, trimmed to that
piece (Tessellation::with_pieces, Shape::tessellate_pieces); node
relaxation slides on it the same way.

Test from commands: the enclosure base's four slivers absorbed, meshed
whole and by symmetry, the absorbs kept in every cell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 15:25:37 -04:00
jyvikoandClaude Opus 5.5 09385f069f geometry: a face merge applies to the cells of a partitioned body
A merge on `Base` failed with "no body `Base`" as soon as partitions cut
the body into cells (mesh.plan.apply's symmetry and corner cuts), so the
plan's cells and the body as opened silently lost every absorb and merge.
Each cell now takes the merge where the pieces of its faces touch (named
after the face kept, else merge:<name>[#k]), and the body as opened keeps
its merges whether partitions cut it or not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 15:25:37 -04:00
jyvikoandClaude Opus 5.5 cb4141d0f0 testkit: run the meshing skill on the corpus and samples (cargo xtask skill-results)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 14:57:51 -04:00
jyvikoandClaude Opus 5.5 bb3def1cf5 mesh.plan: decomposition proposals for unmapped features
For a body with features the sweep cannot mesh, mesh.plan replies a
decomposition: the cuts that box those features into one cell, the region,
and the commands that make the cuts and mesh the cells with pyramids. A plate
with a joined dome: 5 cuts, 17 hex cells swept together, the dome cell tet,
valid and conforming, end displacement in tension within 0.4% of all-tet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 14:57:41 -04:00
jyvikoandClaude Opus 5.5 72ebd5bc7f mesh.cells: tet cells meet hex cells through pyramids
With "transition": "pyramid" (the default; "tie" keeps the old join) a tet
cell is meshed after the hex cells, with a pyramid on every hex face of its
cuts and its tetrahedra on the pyramids' sides: one conforming mesh, no tie.
Where it cannot be built the cell says why (no_pyramids) and is tied. The
apex height is 0.4 of the quad's edge, so pyramids on faces meeting at a
right angle do not cross.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 14:57:41 -04:00
jyvikoandClaude Opus 5.5 3da332be77 ui: pyramid colour stands out from the selection pinks (fix develop CI)
The pyramid5 colour added in #517 sat within the selection test's distance of
the hover pink (dark and light). Darker violet in both themes; the headless
palette matches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 14:56:10 -04:00
jyvikoandClaude Opus 5.5 4908f5679c mesh: strong relaxation morphs round the bad elements (harmonic interpolation within a reach)
The owner's direction: bad nodes move to better places, the nodes within a reach follow by
the graph Laplacian's harmonic extension, edges held, nothing outside the reach moves.
mesh.node.relax strong mode takes below and reach and reports each round.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 14:55:59 -04:00
jyvikoandClaude Opus 5.5 6f94346253 mesh: mesh.node.relax mode strong, below and layers; partitioned bodies count as their cells
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 14:55:59 -04:00
jyvikoandClaude Opus 5.5 b3a7f12472 mesh: a closed edge's start is no curve end of the swept cross-section (#512)
The extrude/revolve cross-section took both ends of every B-rep edge as curve ends. A closed
edge without a vertex on it (a hole's rim after geometry.faces.merge of its halves) starts
wherever its chain was picked up, the old seam vertex, so that point stayed a curve end and a
node column stayed on the merged boundary. Only open edges' ends (and the B-rep vertices) are
curve ends now; the layout and interval assignment place the rim's nodes.

The hex test runs on a square plate and asserts the seams carry exactly as many nodes as the
same lines turned a quarter round the hole (10 and 10; before 5 and 10): the corner layout
puts arc-midpoint nodes there by symmetry, so 0 is not the right target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 13:39:24 -04:00
jyvikoandClaude Opus 5.5 3a5a8213a1 fem: pyramid5 element (solver, quality, decks, VTK)
A five-node pyramid, the conforming transition between a hexahedron's face
and tetrahedra: Bedrosian's rational shape functions with a collapsed (Duffy)
Gauss rule, a certified Jacobian on the base square with exact volume, a
scaled Jacobian, C3D5 / CPYRAM / LS-DYNA degenerate solid, VTK_PYRAMID and
the binary mesh code 13. A distorted hex8-pyramid5-tet4 transition mesh
passes the patch test and uniaxial tension exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 12:48:01 -04:00
jyvikoandClaude Opus 5.5 4e6fd59409 engine: geometry.stitch, imported surfaces stitched for meshing (#507)
geometry.stitch {name, surface, tolerance} records a stitch; the
surfaces keep what was read (TaggedSurface::original) and are stitched
again from it whenever a surface is imported, the model opened, or a
stitch added or deleted (geometry.stitch.delete). Opening a model also
warns about merges that no longer apply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 12:07:16 -04:00
jyvikoandClaude Opus 5.5 793fbe3b71 mesh: corner copy keeps the best of several cuts, its corner taken from the whole cell's mesh
The whole cell is meshed with its layout split at each candidate cut and
the sheets there held planar (ProjectOptions::planes); the corner of the
best is added as the low cell's mesh with the whole mesh's face tags,
mirrored, and the middle meshed round it. Accepted only when the corner
and the middle are no worse than the cell meshed whole; else the cell is
meshed whole and corners.skipped says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 12:07:04 -04:00
jyvikoandClaude Opus 5.5 db0f0567a0 mesh: the corner copy takes the corner from the whole cell's mesh, sheets at the cuts held planar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 12:07:04 -04:00
jyvikoandClaude Opus 5.5 fea61fb180 mesh: a symmetric plan copies one corner onto the other (#474, #491)
With symmetry, when the fundamental cell's two ends along another in-plane
axis are alike (layout planes and surface mirror-symmetric), the cell is
cut midway into the next block sheet and at its image; the low corner is
meshed with its blocks, mirrored onto the high one, and the middle meshed
with both cut faces fixed (#490). A plan cell's cut faces another cell's
mesh covers are taken as given. corners: false opts out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 12:07:04 -04:00
jyvikoandClaude Opus 5.5 58174ba168 geometry.face.absorb: tiny and sliver faces into a neighbour (#506)
A face merge may keep one of its faces' names (Merge::into, model
FaceMerge::into): absorbing a tiny face leaves the neighbour by its own
name, so sets on it still select it. Shape::absorbing_neighbour finds
the face a tiny one shares the longest edges with. geometry.face.absorb
absorbs one face (into a named or the best neighbour) or, from_check,
every small and sliver face the last geometry.check marked; check marks
now carry the small face they found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 11:13:23 -04:00
jyvikoandClaude Opus 5.5 ce02d263c7 engine: geometry.faces.merge, faces meshed as one (virtual topology, #505)
geometry.faces.merge {name, faces} records a face merge in the model;
it is applied after the partitions whenever the design is opened or cut
again, undoes through the journal and is saved with the model. Meshers,
mesh.plan (features and rings see merge:<name>; a merged face is
prismatic when its B-rep faces are), checks and the viewport see one
face, drawn in the warm colour. geometry.faces.merge.delete removes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 11:09:11 -04:00
jyvikoandClaude Opus 5.5 e4233ff794 mesh: format
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 10:55:27 -04:00
jyvikoandClaude Opus 5.5 6f07e4c09c mesh: the projection mesher takes fixed boundary patches as given (#490)
A patch of quads already meshed on a face of the body (a neighbour's mesh)
is matched to the block faces on it: block vertices to its nodes (its
singular nodes first, then along its grid lines), block edges along its
grid lines, faces filled quad by quad. Those faces take the patch's
divisions and nodes, held through smoothing. mesh.hex.project takes
"fixed": [face names] and builds the patch from other bodies' meshes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 10:55:27 -04:00
jyvikoandClaude Opus 5.5 d5fb100182 mesh.plan.apply: name the faces of the meshed bodies left untagged
The reply's untagged_faces lists every face of the plan's bodies no
element face carries; the acceptance checks require it empty, and a
symmetric filleted base checks a set by every face of every cell (#494).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 10:38:31 -04:00
jyvikoandClaude Opus 5.5 937e557e5a mesh.mirror: carry each face tag to the image face its reflection covers
The image's face names came from one sample per source face (the first
element face's centre), so a source face whose sampled element face was
off it, or a sliver, mapped wrong and the true image face went untagged.
Each source face now maps to the image faces its own reflection covers,
and each element face to the nearest of those.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 10:38:31 -04:00
jyvikoandClaude Opus 5.5 7c3107dec0 mesh: a crank arm's plan takes rings round its bosses and a pillow from a carved grid (#455, #190)
When the grid layout does not fit a swept body (sides at an angle to the axes, root arcs where
an arm meets its bosses), mesh.plan tries the ring-and-pillow layout (cmd/plan/pillow.rs): each
boss is an annulus from its bore or pedal hole to its circle through every level, the arm a grid
carved a margin inside the outline and the bosses, with a pillow layer of blocks to the wall each
boundary face faces; a grid corner facing a boss and the outline gets a corner block reaching the
kink where the outline leaves the boss's circle. The old crank test (frame cuts) becomes the
heavy acceptance test at sizes 2.5, 3 and 4; the moved frame cut keeps a unit test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 10:38:26 -04:00
jyvikoandClaude Opus 5.5 992bc71b6b engine, render: symmetry plane command on any datum plane (#242)
model.boundary_condition.symmetry takes a datum plane of any orientation,
finds the faces on it and reports the plane's unit normal when it is
inclined; the glyph draws a support along that normal. Verified: half a plate
cut on its diagonal with the plane deflects as the half mirrored across it
(6.7e-13), the mirror conforming on the plane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 10:38:20 -04:00
jyvikoandClaude Opus 5.5 a079e51f9d repo: move to github.com/tangency-labs/fieldcraft (URLs, updater, Pages)
The updater's release endpoint, docs, README, release notes and the Pages
URL point at the tangency-labs org. App IDs (io.github.jyviko.fieldcraft)
stay unchanged so installed copies keep their identity and settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 10:38:13 -04:00
jyvikoandClaude Opus 5.5 b74d15e44d mesh: relax by face names the listed faces with no mesh (#471)
Faces no element is tagged with are left out and named in the reply
(faces_without_mesh); none tagged is the error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 09:21:21 -04:00
jyvikoandClaude Opus 5.5 7d269c58f9 ui: Relax Nodes takes picked faces (#471)
Use selection puts the selected design faces in the form as faces, in
place of a sphere; Shift/Ctrl-click picks faces while the form is open,
and every click does once the form lists faces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 09:21:21 -04:00
jyvikoandClaude Opus 5.5 3ee622f62a mesh: relax nodes by CAD face; params docs match their forms (#471)
mesh.node.relax takes faces: [{body, face}] (every node on those B-rep
faces and the nodes of the elements touching them, sliding on the faces)
and elements in its form; the undocumented-by-form set is dropped.
A schema test checks every top-level key a params doc names is accepted,
which also found mesh.hex.extrude normal, mesh.hex.project max_arc_angle
and mesh.plan.apply name missing from their forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 09:21:21 -04:00
jyvikoandClaude Opus 5.5 78b8fc8d6c engine: one scene builder for the window and headless pictures
engine::view::build (Style, Show, Palette) is the viewport's builder,
moved from ui-egui; ui-egui maps theme::Tokens onto a Palette and calls
it. Palette::LIGHT now equals the light theme's tokens. Test: the app's
scene and engine::view::frame's are identical for the same session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 08:29:38 -04:00
jyvikoandClaude Opus 5.5 eae709b3c0 mesh: a body the user's partitions cut is not meshed by symmetry (#473)
With a user's partition on a symmetric body, applying the whole body's plan cut it again by its
symmetry plane and mirrored one cell, leaving the other half unmeshed ("the mirror images do not
reach cell Base/b/a"). Such a body is its cells: the symmetry is not used and the plan is refused
with the cells named, as the replay of the saved Pi 5 model shows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 07:56:10 -04:00
jyvikoandClaude Opus 5.5 80da2d968b mesh: applying a whole body's plan to a body partitions cut says to plan a cell (#473)
After geometry.partition cuts a body, the body is its cells: a projection plan of the whole body
could not open it and failed with "no body `Base` (open a design first)". It now names the cells
and says to plan and apply a cell (mesh.plan {"body": "Base/a"}) or delete the partitions. Tests
cover a design opened directly and a saved model opened again, partitioned after opening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 07:56:10 -04:00
jyvikoandClaude Opus 5.5 b905f1bc89 mesh: a projection plan meshes a symmetric body by its symmetry; partitioned bodies and cells plan (#473)
mesh.plan.apply finds the planes a projection plan's body is its own mirror image across
(through the middle of its extent, square to the sweep's in-plane axes, through no ring: every
triangle middle of its tessellation, a little inside and outside, has its mirror image inside and
outside alike). It cuts the body by them, plans and meshes the cell on their negative side, and
mirrors that mesh across each plane in turn (mesh.mirror, joined node to node on the planes), so
mirror-image regions mesh exactly alike. `symmetry: false` meshes the whole body.

A cell of a partitioned body is planned from the features that built its body, with the rings it
holds; a partitioned body is planned whole as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 07:26:28 -04:00
jyvikoandClaude Opus 5.5 1f183e04ea engine: aim a picture's camera at a target (#467)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 06:28:57 -04:00
jyvikoandClaude Opus 5.5 cbd60a7985 results: results.xy, a quantity against the load factor, time or mode (#94)
A nodal field at a node per frame (nonlinear static displacements from
load factor 0, explicit frames, heat temperatures) or per mode, the
explicit energies and histories, history nodes, and the frequencies or
buckling load factors; capped at 100000 points with `every`; stale
results refused; `path` writes a two-column CSV. XY Plot is ready.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 04:33:06 -04:00
jyvikoandClaude Opus 5.5 47e92eebb1 mesh: a plan's frame cut on an opening's end face moves one element out of it (#455)
The crank arm's back pocket ends on the planes x = 48 and x = 130, its frame sides. The plan
put its frame cuts there; the kernel cannot cut along a face (#427), so both were skipped and
the cell between the bosses swept the pocket's rounded ends into fans. A frame side on a face
of the body now moves one element out of the opening (away from the rectangle's middle),
leaving a strip one element wide; both cuts are made.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 03:59:29 -04:00
jyvikoandClaude Opus 5.5 bf61ae8406 mesh: smoothing visits nodes in their own order again (#402)
#462 visited nodes by a mirror-invariant key (distance from the centroid). On the cup with a
handle that order sweeps ring by ring round the axis and converges worse in its 100 sweeps: the
refined cup fell to a scaled Jacobian of 0.481 (needs 0.5, #440). In the lattice's own order
it holds, and the order gained the enclosure base little (its corners differ by 0.02 either
way), so it goes; the compass search's lift to 0.7 stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 03:42:47 -04:00
jyvikoandClaude Opus 5.5 4d6d69ee6d mesh: smoothing lifts elements up to 0.7 and visits mirror twins alike (#402)
The compass search after the sweeps only lifted elements below 0.5 (the quality floor), so a
mesh whose worst elements sat just above it stayed there: every corner of an enclosure base
stopped at a scaled Jacobian of 0.50-0.52 where its walls meet the corner bosses. It now lifts
every element below 0.7, never lowering another below the lower of its own value and 0.5.

The sweeps and the search are Gauss-Seidel, so the node order shaped the result and mirror
image corners smoothed differently. Nodes are now visited by a key that a mirror or a quarter
turn about the nodes' centroid keeps (distance from it, then the sorted offsets along the
axes). What spread is left between mirrored corners (0.686 vs 0.702 on the test base) is most likely the projection onto the body's
tessellation, which the kernel does not make mirror symmetric (not yet proven).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 03:05:43 -04:00
jyvikoandClaude Opus 5.5 e79ba866a1 engine: draw geometry.check findings as viewport markers
geometry.check keeps its findings on the session (not an edit: not journalled,
saved or undone) while the checked design stays open; the picture and the app's
viewport draw the faces of small, sliver and near faces in the marker colour,
short edges as thick lines and a dot at each finding over everything. `mark:
false` or a clean check clears them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 02:47:14 -04:00
jyvikoandClaude Opus 5.5 f8b92fcd89 ui: element faces and edges as element:side lists
A Sides field kind: element faces or edges typed as element:side with
ranges (1-20:3, 25:1), no row cap, and Use selection for picked faces.
Set and mesh-tag forms use it instead of a JSON box (#449).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 02:30:24 -04:00
jyvikoandClaude Opus 5.5 9b705f5334 ui: fluid boundary face and obstacle faces as typed fields
The boundary's 'at' is a box face or an obstacle body and face
(NameOr); the domain's obstacle faces are rows of a face and its wall
condition (#449).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 02:14:58 -04:00
jyvikoandClaude Opus 5.5 98e96921e6 ui: mirror plane and images as typed fields
Two field kinds: NameOr (a name as a bare string, or an object of fields)
and RefText (a text per item of a collection). The mirror's plane is a
datum plane or a point and normal; its images a body name per body
(#449).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 01:51:04 -04:00
jyvikoandClaude Opus 5.5 d17cf414f7 ui: block divisions and bias as two or three typed fields
A Tuple field kind: min_len to labels.len() positive numbers, whole when
asked, trailing blanks left out. The block's divisions and bias use it
instead of a JSON box (#449).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 01:42:51 -04:00
jyvikoandClaude Opus 5.5 e4d1f6c7da ui: one number or one per axis for conductivity, expansion and point mass
A PerAxis field kind: one value for every direction, or Per axis ticked
for one per axis. Thermal conductivity and expansion and the point mass
use it instead of a JSON box (#449).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 01:38:26 -04:00
jyvikoandClaude Opus 5.5 61f0d42ec4 ui: nonlinear load path as a table of fraction and factor
Kind::Points gains its columns, so a list of number rows other than
[x, y, z] gets the same typed editor; the load path uses it (#449).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 01:34:38 -04:00
jyvikoandClaude Opus 5.5 ed25de6521 engine: crash reports name the startup step, the renderer and a native crash (#299)
The journal keeps the startup step the app is in (phase) and the renderer
(set_environment) across rebases; an unclean exit before the first frame is
reported "during startup (<step>, N s after launch)". native_paths() is where
a native exception filter writes what it caught and a minidump; the next
start puts the exception code, module+offset and thread into the report and
moves the minidump into its folder. Adapted from SolidCraft's crash.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 01:11:58 -04:00
jyvikoandClaude Opus 5.5 ead489592c ui: arc-length displacement limit as typed fields
The step form's displacement limit was a raw JSON box; it is now a group
of node, direction and displacement fields (#449).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 00:50:12 -04:00
jyvikoandClaude Opus 5.5 9a30c7d9ed mesh: the one-to-one sweep names a body that ends at several levels (#444)
The crank ends at 8, 16, 20 and 22 mm, so no one-to-one sweep takes it.
The sweep met one of those caps as a linking face and reported "linking
face has no top side". A plane parallel to the source that only part of a
ring reaches is now named as what it is, with extrude pointed to. The arm's
profile alone sweeps as well as extrude does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 00:33:55 -04:00
jyvikoandClaude Opus 5.5 0b7dda95f7 mesh: an enclosure's corner bosses are ringed where its walls join them (#402)
A boss the body's walls run into had its circle crossed by the walls' planes, so the projection
layout refused it and the plan fell back to swept cells, fanning round the boss and its hole.
Such a boss is now framed by the square round its whole circle, subdivided by the planes that
cross it; every square inside (the hole's, the core) is divided alike, so the ring's blocks meet
the walls node to node. On the walls' side the frame stops at their inner plane and its corner
projects onto the middle of the inner fillet, which the ring takes in (a boss just touching the
fillet included: no sliver). Cylinders a fillet's history names but that are a ring's circle are
the ring's faces. Rings are built per patch of grid cells, the one-cell O-grid being the
simplest patch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 00:29:24 -04:00
jyvikoandClaude Opus 5.5 3d38753cad geometry: a partition the kernel can only nudge off its plane is refused (#427)
The kernel's last-resort boolean retry moves the tool 1.3 µm and turns it
2 µrad. For a partition that puts the cut beside the face it was meant to
run along and leaves a cell bounded by slivers (the open boundary at x = 130
on the crank half). halves() now refuses a result-altering retry and names it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 00:20:02 -04:00
jyvikoandClaude Opus 5.5 1af87f1f98 mesh: a disk's O-grid follows the region's elongation (#198)
A smooth loop (straight sides running tangent into arcs, as a slot or a
rounded pocket) is meshed as a disk. Its middle block was a square halfway
to the loop's quarter points, which on the crank's 6:1 pocket floor
squashed elements to a scaled Jacobian of 0.16. The block is now a
rectangle on the region's principal axes, inset by the same distance all
round (the old square on a circle), its corners paired with the loop nodes
nearest their diagonals, its sides spaced as the nodes they pair with, and
the O-grid is smoothed (untangled first) before the validity check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-15 23:55:02 -04:00