Skip to content

feat(fuse): "Check mirrored parts" — an option for files that store mirrored parts already facing outward - #67

Merged
sswelm merged 2 commits into
masterfrom
fix/fuse-mirrored-judge
Sep 19, 2026
Merged

sswelm merged 2 commits into
masterfrom
fix/fuse-mirrored-judge

Conversation

@sswelm

@sswelm sswelm commented Sep 19, 2026

Copy link
Copy Markdown
Owner

Summary

"There is an issue with Group A, it's partly transparent despite having all parts" (the Confederate frigate), then "make it an option".

glTF says a mirrored part (a negative-scale node, one side of a symmetric hull) renders with its winding reversed, and the fuse applies that to every mirrored part, which is exactly what the Teutonic needed. The frigate's file stores its mirrored hull half already facing outward, so the same reversal turned that whole side inward. Welded to the correct half in one island, the two cancelled every direction signal and the fuse left the island as it stood.

frigate group A, rays along the beam toward −Y toward +Y
fused, as today 94.8 % back-facing 0.2 %
the same parts unfused 0.1 % 0.1 %

The option, "Check mirrored parts", in the Model Fuser under the weld slider. Off by default. After welding, a plain part's winding is the reference, since no rule touched it. A mirrored part whose shared seams run the same way as its plain neighbours' is inside-out relative to them. The reversal is undone only on a clear verdict: at least three mirrored parts judged and nine in ten agreeing. Then every mirrored part follows, except one whose own seams disagree. Mixed evidence changes nothing. With the option off the fuse warns when a group would need it.

Measured with the real fuse on the real files

file, group option off option on
frigate A, hull 94.8 % back-facing 0.0 %
Romanic A, hull, from the beam 108 / 9 cells 8 / 5
Romanic D, deck, from above 161 cells 29
Romanic H (judged 1 pre-flipped against 2 standard) byte-identical
Teutonic hull, eight other Romanic groups byte-identical

The Romanic's 108 hull cells, taken this morning as authored in the source, turn out to be the same bug.

Two rules came out of the drills rather than the design:

  • The file's convention. A per-part rule left the frigate's Object_961, which touches no plain part, on the reversal. It carried 2,443 of the 2,462 back-facing cells that remained. How mirrored parts are stored is a property of the exporter, so a clear verdict from the judged parts decides the rest.
  • Clear verdicts only. Acting on a single part in the Romanic's group H, whose judged parts disagreed, made it worse, from 2,667 to 2,955 back-facing cells. Now mixed evidence changes nothing.

Developed and drilled in a separate worktree that the Unity project does not compile.

Test plan

  • bash tools/check.sh (861 tests). New: a pre-flipped file fixed only with the option; a standard file byte-identical; mixed evidence unchanged; a lone mirrored part following the file's convention, its failure reproduced with the option off.
  • With the option off, the frigate's output is byte-identical to master.
  • Model Fuser on the frigate: fuse without the option and read the warning, then tick Check mirrored parts, fuse, and check the hull in the Vehicle Lab: both sides solid.
  • The Romanic with the option on: the hull and deck as good or better, everything else unchanged.

🤖 Generated with Claude Code

sswelm and others added 2 commits September 19, 2026 21:52
…heir mirrored parts already facing outward

User, on the Confederate frigate: "there is an issue with Group A, it's
partly transparent despite having all parts", then "make it an option".

glTF says a negative-determinant node renders with its winding
reversed, and the fuse gather applies that to every mirrored part —
which is what the Teutonic needed. The frigate's file stores its
mirrored hull half ALREADY facing outward, so the same reversal turned
that whole side inward. Measured (rays along the beam, 0.05 cells):

  fused, as today     94.8 % back-facing from one side, 0.2 % the other
  same 60 parts unfused   0.1 % / 0.1 %

Welded to the correct half in one island, the halves cancelled every
direction signal (volume +0.00, inside-out +0.00, twins 3605 / 3514),
the island was "not judged", and the fuse kept it as it stood.

Step 3b, after welding: a PLAIN part's winding is never in doubt, so it
is the reference. A mirrored part whose shared two-face seams run the
same way as its plain neighbours' is inside-out relative to them;
verdicts spread ring by ring to mirrored parts that touch only
mirrored ones. The reversal is undone only on a CLEAR verdict: at least
three parts judged and nine in ten agreeing the file is pre-flipped.
Then every mirrored part follows (parts with no plain neighbour
included, the frigate's Object_961 carried 2,443 of the 2,462 cells a
per-part rule left behind), except a part whose own seams disagree.
Mixed evidence changes nothing: the Romanic's group H judged 1 against
2, and acting on the one made it worse (2,667 -> 2,955 cells).

OFF BY DEFAULT: with it off the output is byte-identical to master
(checked on the frigate); the fuse warns when a group would need it.
With it on, measured with the real fuse on the real files:

  frigate group A            94.8 % -> 0.0 % (unfused source 0.1 %)
  Romanic group A, hull      108 / 9 -> 8 / 5 back-facing cells from the
                             beam, same bug, earlier taken as authored
  Romanic group D, deck      161 -> 29 from above
  Romanic group H            byte-identical (mixed evidence)
  Teutonic hull, 8 other Romanic groups   byte-identical

Developed and drilled in a separate worktree the Unity project does not
compile. Four tests: a pre-flipped file fixed only with the option, a
standard file byte-identical, mixed evidence unchanged, a lone mirrored
part following the file's convention (its failure reproduced with the
option off). A Model Fuser checkbox under the weld slider; changelog
and tools page. 861 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lying on their plates are not inside-out

Review of PR #67 (76de8e3), P2, reproduced and fixed.

A lap strip lies ON its plate, stitched along one edge and facing the
same way, so both faces walk the shared edge the SAME way on purpose.
The consistency pass has handled that since 2026-09-15 (the Teutonic's
Object_8, 671 strips): same traversal with surfaces already facing the
same way (normalised dot > 0.9) is consistent as authored. Step 3b
counted it as a conflict, so three mirrored lap strips over plain
plates drew a unanimous "already facing outward" verdict and 18
correct faces were turned inward.

3b now applies that same rule before it counts a conflict, a decided
part about to be undone judged as it will be written. One rule for
laps in both places, not a second one.

Test: the reviewer's case, three mirrored lap strips on plain plates:
every face +Z with the option off (premise), the output byte-identical
with it on, no verdict, no warning. It failed before the fix.

Re-drilled on the real ships, in a worktree the Unity project does not
compile: the frigate's group A byte-identical to the result that
measured 0.0 % back-facing; the Teutonic hull identical to off; all
eleven Romanic groups identical to the previous on-run, so the hull
(108 -> 8) and deck (161 -> 29) gains stand. 862 tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sswelm

sswelm commented Sep 19, 2026

Copy link
Copy Markdown
Owner Author

Confirmed and fixed in 516995e — thank you, it reproduced exactly as described.

A lap strip lies on its plate, stitched along one edge and facing the same way, so both faces walk the shared edge the same way on purpose. The consistency pass has handled that since 09-15 (the Teutonic's Object_8, 671 strips): same traversal with surfaces already facing the same way (normalised dot > 0.9) is consistent as authored. Step 3b counted it as a conflict. It now applies that same rule before counting — one lap rule in both places, not a second one — with a decided part about to be undone judged as it will be written.

Your case is the new regression test: three mirrored lap strips on plain plates — every face +Z with the option off, output byte-identical with it on, no verdict, no warning. It failed before the fix.

On the caveat that the real ships were not exercised: I re-drilled them, in a worktree the Unity project does not compile.

real file, option on after the lap fix
frigate group A byte-identical to the result that measured 0.0 % back-facing
Teutonic hull identical to option off
all 11 Romanic groups identical to the previous run — hull 108 -> 8 and deck 161 -> 29 gains stand

862 tests pass; gate passed on the push.

🤖 Generated with Claude Code

@sswelm
sswelm merged commit 61a98e3 into master Sep 19, 2026
2 of 3 checks passed
@sswelm
sswelm deleted the fix/fuse-mirrored-judge branch September 19, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant