Commit Graph
6 Commits
Author SHA1 Message Date
ajandClaude Fable 5.1 8f27eac2ca feat(cutting): plan each scrap cut-off segment where the route passes it
Plates with staged automatic scrap cut-offs could not be planned: P260810-01
plate 1 ran out of budget even at 10x effort, while the same plate plans
Ready in 3.6k expansions without the lines. A cut-off was one fixed program
cut whole before every part its line passes through, which dragged the tour
to the middle of the sheet and left the head at the far edge of the line;
and cutting every line first (the rejected alternative) weakens the sheet
and breaks the containment operators rely on when they fast-forward a
changed program.

A cut-off stays one definition, but Plate.RegenerateCutOffs now makes one
part per trimmed segment, each with its own place in the cut sequence.
CutOffSegments gives each segment the span of the nominal line it stands
for (end of the segment before it to start of the one after), and the
dependency rules of the planner and Part Sequencing use that span, so a
segment precedes only the parts beside it. The free-order search plans the
parts as if the segments were not there, so adding lines no longer changes
a plate's route, then CutOffInsertion slides each segment into the latest
gap before its first part where the rapid into its pierce and the rapid
from its end cross nothing cut before; only when one fits nowhere is the
plate searched with the segments among the parts. The search looks through
segments wherever they sit (entries face the next part, hole routes rank
from the pre-segment position, rapids are still checked from the segment's
end), so an applied plan replanned with the order kept replays Ready.
Segments saved out of consecutive order are recorded per segment in the
.nest file (`segments`); older readers load them consecutively.

P260810-01 plate 1 now plans Ready in 3639 expansions with every check
passing; rapid travel 1836 against 1547 without the lines. Plates without
cut-offs keep their PlanningCharacterizationTests digests, and the
problem-nest corpus has no plate below its baseline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 11:38:38 -04:00
aj 094c4c196b fix(bestfit): never reject pair candidates by utilization
Thin-framed, hollow, or concave parts (e.g. SULLYS-035's frame) have
inherently low part-to-bbox utilization yet nest tightly, so the 30%
MinUtilization floor wrongly dropped every candidate for them. Pair
quality is judged by the rotated pair bounding-box area the results
are already sorted on; utilization now only ever serves as the
high-aspect exception (UtilizationOverride), never as a rejection.

Adds a hollow-frame helper plus regression tests that kept pairs
exist with low utilization and results stay sorted by pair area.
2026-09-26 14:14:44 -04:00
ajandClaude Opus 4.8 e493d83899 feat(io): add Gravograph .CHR font reader with text-to-geometry
Add ChrFont, a reader for Gravograph .CHR engraving fonts, plus UI to
convert placed text into engraved geometry in the CAD converter.

The .CHR files are obfuscated with a single-byte XOR. Different
GravoStyle releases use different keys (0x2F in older versions, 0xCF in
the 7000 series, and others across the font library), so the key is
auto-detected from byte 1 of the file: the font name is ASCII stored as
UTF-16LE, so the high byte of its first character is 0x00 in plaintext
and the raw byte equals the key. This reads every font in a GravoStyle
install regardless of version, not just one hardcoded key.

UI: right-clicking a text item in EntityView raises TextConvertRequested;
CadConverterForm renders it via ChrFont with H/V alignment and adds the
result on an ENGRAVE layer.

Tests use Xunit.SkippableFact and a gitignored test-config.json so the
suite points at a local .CHR file without committing proprietary assets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 14:37:48 -04:00
ajandClaude Opus 4.6 a576f9fafa fix: correct test helper winding and edge sequencer test data
Test drawings used CCW winding, causing OffsetSide.Left to produce
inward offsets. The BestFit pipeline then positioned pairs so actual
shapes overlapped, failing all 1232 candidates. Changed to CW winding
to match CNC convention where OffsetSide.Left = outward.

Also fixed EdgeStartSequencer test: centerPart at (25,55) was only 4.5
from the top edge (plate Y=60), closer than midPart at (10,10). Moved
to (25,25) for correct ordering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:31:50 -04:00
ajandClaude Sonnet 4.6 71f28600d1 refactor: extract PolygonHelper from AutoNester for shared polygon operations
Creates PolygonHelper.cs in OpenNest.Engine.BestFit with ExtractPerimeterPolygon
(returning PolygonExtractionResult with polygon + correction vector) and RotatePolygon.
AutoNester.ExtractPerimeterPolygon and RotatePolygon become thin delegates.
Adds MakeSquareDrawing/MakeLShapeDrawing to TestHelpers and 6 PolygonHelperTests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 19:56:20 -04:00
ajandClaude Sonnet 4.6 00940d1b6e chore: rename test project to OpenNest.Tests
Renamed OpenNest.Engine.Tests → OpenNest.Tests (directory, .csproj,
namespaces in all .cs files). Added OpenNest.IO project reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 00:25:22 -04:00