From 3f4196464e5095d58baacddff967e33f1aeed00d Mon Sep 17 00:00:00 2001 From: Colin Sharples Date: Fri, 25 Sep 2026 07:18:41 +1200 Subject: [PATCH 1/5] Normalise the free canvas fragments edited in Inkscape The content changes in these four are CTG's: each Cyberspace fragment now keeps only the drawn control measure in #main, with the illustrative endpoints and net boxes moved into a hidden example group and the anchor points into the template. That is the shape #78 is about, applied by hand to the three Cyberspace icons. Inkscape rewrites a file wholesale on save - attribute per line, standalone="no", its own namespace declarations, a namedview, an empty defs and a fresh crop of generated ids - so all four needed the normaliser afterwards. It reported 0 refused, which is the guarantee that wanted: the drawn content is byte-for-byte what CTG left, and only the editor's own leavings were taken out. One follow-on belongs with this rather than with the mechanical pass: 60200100's example group carried id="g15", which the normaliser correctly reads as a generated id and drops, leaving the group anonymous. It is given id="example" so it says what it is. Bounds re-measured. 60200100 and 60200200 grew taller, which is CTG's edit - the template's anchor arrows now reach above the drawn measure. Nothing else moved. Co-Authored-By: Claude Opus 5 --- .../src/main/resources/model-hallux.yml | 4 +- .../src/main/resources/model-standard.yml | 4 +- .../ctgnz/jmsfx/standard/IconBounds.java | 4 +- .../Appendices/ControlMeasures/25110300.svg | 54 ++++++++++--------- .../svg/Appendices/Cyberspace/60200100.svg | 29 ++++++++-- .../svg/Appendices/Cyberspace/60200200.svg | 40 ++++++++++---- .../svg/Appendices/Cyberspace/60210100.svg | 42 +++++++++++---- 7 files changed, 120 insertions(+), 57 deletions(-) diff --git a/jmsfx-generator/src/main/resources/model-hallux.yml b/jmsfx-generator/src/main/resources/model-hallux.yml index a0e8465d..3a2e4d95 100644 --- a/jmsfx-generator/src/main/resources/model-hallux.yml +++ b/jmsfx-generator/src/main/resources/model-hallux.yml @@ -1287,8 +1287,8 @@ iconBounds: "25360200": {minX: 227.28, minY: 185.97, width: 252.23, height: 234.48} "25360300": {minX: 223.19, minY: 129.96, width: 249.97, height: 308.29} "25370100": {minX: 9.5, minY: 54.99, width: 379.73, height: 281.13} - "60200100": {minX: 114.28, minY: 133.42, width: 371.45, height: 133.17} - "60200200": {minX: 88.87, minY: 92.18, width: 410.98, height: 172.99} + "60200100": {minX: 114.28, minY: 110.55, width: 371.45, height: 166.59} + "60200200": {minX: 88.87, minY: 70.67, width: 410.98, height: 215.52} "60210100": {minX: 88.61, minY: 90.65, width: 373.87, height: 229.45} modifierBounds: "10622": {minX: 121.0, minY: 442.52, width: 370.0, height: 5.28} diff --git a/jmsfx-generator/src/main/resources/model-standard.yml b/jmsfx-generator/src/main/resources/model-standard.yml index a3d7a2c7..ce321c5d 100644 --- a/jmsfx-generator/src/main/resources/model-standard.yml +++ b/jmsfx-generator/src/main/resources/model-standard.yml @@ -1225,8 +1225,8 @@ iconBounds: "25360200": {minX: 227.28, minY: 185.97, width: 252.23, height: 234.48} "25360300": {minX: 223.19, minY: 129.96, width: 249.97, height: 308.29} "25370100": {minX: 9.5, minY: 54.99, width: 379.73, height: 281.13} - "60200100": {minX: 114.28, minY: 133.42, width: 371.45, height: 133.17} - "60200200": {minX: 88.87, minY: 92.18, width: 410.98, height: 172.99} + "60200100": {minX: 114.28, minY: 110.55, width: 371.45, height: 166.59} + "60200200": {minX: 88.87, minY: 70.67, width: 410.98, height: 215.52} "60210100": {minX: 88.61, minY: 90.65, width: 373.87, height: 229.45} modifierBounds: "10622": {minX: 121.0, minY: 442.52, width: 370.0, height: 5.28} diff --git a/jmsfx-standard/src/main/java/io/github/ctgnz/jmsfx/standard/IconBounds.java b/jmsfx-standard/src/main/java/io/github/ctgnz/jmsfx/standard/IconBounds.java index 926fa70f..411ff9d6 100644 --- a/jmsfx-standard/src/main/java/io/github/ctgnz/jmsfx/standard/IconBounds.java +++ b/jmsfx-standard/src/main/java/io/github/ctgnz/jmsfx/standard/IconBounds.java @@ -594,8 +594,8 @@ public final class IconBounds { Map.entry("25360200", new Rectangle2D(227.28, 185.97, 252.23, 234.48)), Map.entry("25360300", new Rectangle2D(223.19, 129.96, 249.97, 308.29)), Map.entry("25370100", new Rectangle2D(9.5, 54.99, 379.73, 281.13)), - Map.entry("60200100", new Rectangle2D(114.28, 133.42, 371.45, 133.17)), - Map.entry("60200200", new Rectangle2D(88.87, 92.18, 410.98, 172.99)), + Map.entry("60200100", new Rectangle2D(114.28, 110.55, 371.45, 166.59)), + Map.entry("60200200", new Rectangle2D(88.87, 70.67, 410.98, 215.52)), Map.entry("60210100", new Rectangle2D(88.61, 90.65, 373.87, 229.45))); /** diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25110300.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25110300.svg index 38d4aee6..4e57039b 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25110300.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25110300.svg @@ -1,26 +1,30 @@ - - - - EWL - EWL - - - 326 EN BN (USA) - 127 EN BN (USA) - - - - T1/AS1 - - - - T/AS - - - PT 2 - - PT 1 - - - + + + + EWL + EWL + + + 326 EN BN (USA) + 127 EN BN (USA) + + + + T1/AS1 + + + + T/AS + + + + PT 2 + + + + PT 1 + + + + diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60200100.svg b/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60200100.svg index 5fd5fddd..21f3b680 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60200100.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60200100.svg @@ -2,13 +2,14 @@ - + + + diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60200200.svg b/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60200200.svg index 218a6376..bdcb100a 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60200200.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60200200.svg @@ -1,23 +1,41 @@ - - - - - MODNET - - + + - - - - + + + + PT 2 + + + + PT 1 + + + + T + + + + PT 3 + diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60210100.svg b/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60210100.svg index 4a4ab5aa..f1838338 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60210100.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/Cyberspace/60210100.svg @@ -1,17 +1,39 @@ - - - - - MODNET - + + + + - - - - 192.168.25.0 + + + + PT 2 + + + + PT 1 + + + + + + AY + + + + + + T + + + From 12ab45457068c0890f45d388ce3bf2d310a9a910 Mon Sep 17 00:00:00 2001 From: Colin Sharples Date: Fri, 25 Sep 2026 07:18:54 +1200 Subject: [PATCH 2/5] Bring the free canvas fragments onto one shape (#78) A free canvas icon holds a #main, a #template, zero or more #example groups, and whatever the main group needs. That is what almost all of them already were - 545 of 577 - and this brings the stragglers into line so a consumer asking "which element is the content" has one answer rather than four. Three kinds of file were off the shape: - Four put an id straight on a drawing element with no group to hold it: 25218800 and 25281901 on a , and 25200600 and 25200700 on a loose . Each is now wrapped in a carrying that id. - Five define elements loose - four at the document root, and two more inside #main itself, where a paint server has no business sitting. They move into a , which is the one place a definition belongs. - Ordering and ids otherwise left exactly as found. Nothing here changes what is drawn. A carrying only an id paints exactly as its child did, and a renders only where something references it - and nothing does: the whole tree holds one live url(#...) reference, 25132000's marker, which is untouched. The issue's note that the pattern files hold the only internal reference is no longer accurate. Verified two independent ways, since SvgFingerprint cannot help here - it records structure, so inserting a group or moving an element changes it by design: - Every drawing primitive compared before and after with its ancestors' attributes folded in, so an added group is invisible to the comparison, and each pattern subtree compared on its own. All nine identical. - FragmentMeasurer re-run over all 577 free canvas icons: every bound unchanged. Left alone deliberately: 26 fragments have no template group at all, which is a question about what those files draw rather than about their shape, and two patterns in 25270400 have no id, so they cannot be referenced from defs either - naming them belongs with the pattern usage work rather than here. Closes #78 Co-Authored-By: Claude Opus 5 --- .../Appendices/ControlMeasures/25172000.svg | 38 +++++++-------- .../Appendices/ControlMeasures/25200600.svg | 4 +- .../Appendices/ControlMeasures/25200700.svg | 4 +- .../Appendices/ControlMeasures/25218800.svg | 4 +- .../Appendices/ControlMeasures/25240301.svg | 42 +++++++++-------- .../Appendices/ControlMeasures/25240302.svg | 42 +++++++++-------- .../Appendices/ControlMeasures/25240303.svg | 42 +++++++++-------- .../Appendices/ControlMeasures/25270400.svg | 46 ++++++++++--------- .../Appendices/ControlMeasures/25281901.svg | 4 +- 9 files changed, 122 insertions(+), 104 deletions(-) diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25172000.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25172000.svg index e683fc7c..a319621e 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25172000.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25172000.svg @@ -1,23 +1,25 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + TIME TO: diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200600.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200600.svg index f88628e5..c8df8f71 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200600.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200600.svg @@ -1,6 +1,8 @@ - + + + diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200700.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200700.svg index dbcd115c..27c4d38b 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200700.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200700.svg @@ -1,6 +1,8 @@ - + + + diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25218800.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25218800.svg index 6497cbd4..845bbcc4 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25218800.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25218800.svg @@ -1,6 +1,8 @@ - + + + CENTER POINT diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240301.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240301.svg index 4ce60683..9d8dca8b 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240301.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240301.svg @@ -1,30 +1,32 @@ - - - + + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + NFA diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240302.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240302.svg index 15787f44..6d841521 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240302.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240302.svg @@ -1,30 +1,32 @@ - - - + + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + NFA diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240303.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240303.svg index c75ec60e..3073b05a 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240303.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25240303.svg @@ -1,30 +1,32 @@ - - - + + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25270400.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25270400.svg index 10c75017..d969da3c 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25270400.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25270400.svg @@ -1,36 +1,38 @@ - - - + + - + - + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25281901.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25281901.svg index 1c662908..1b174088 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25281901.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25281901.svg @@ -1,6 +1,8 @@ - + + + From a59ee8e1b6258238d7b9cc661f67e4d5840f2fdd Mon Sep 17 00:00:00 2001 From: Colin Sharples Date: Fri, 25 Sep 2026 07:21:18 +1200 Subject: [PATCH 3/5] Call 25200600's grey backdrop an example, not a template (#78) The hidden #474747 rectangle is not part of the construction guide - it is there so the white border of the symbol in #main has something to show up against. The corpus agrees: three fragments carry that backdrop, and the other two, 25200400 and 25200700, both file it under an example group. Id only, so nothing is drawn differently - the rectangle is display="none" in any case, and ids are outside the fingerprint. With this, 551 of 577 free canvas fragments hold only defs/main/template/example*, and every one that does not is simply missing a template. Co-Authored-By: Claude Opus 5 --- .../main/resources/svg/Appendices/ControlMeasures/25200600.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200600.svg b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200600.svg index c8df8f71..a4f7b9ec 100644 --- a/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200600.svg +++ b/jmsfx-standard/src/main/resources/svg/Appendices/ControlMeasures/25200600.svg @@ -1,6 +1,6 @@ - + From 2af52eb2d27377289eb58b96ae245a622fa3ad5e Mon Sep 17 00:00:00 2001 From: Colin Sharples Date: Fri, 25 Sep 2026 07:32:59 +1200 Subject: [PATCH 4/5] Write down the free canvas composition rule (#78) The README's Details section has been a TBD placeholder for "the internal composition of the SVG files" since the fork. Now that the shape is settled and every fragment holds to it, it can say what it is. A free canvas fragment holds exactly one #main, zero or one #template, zero or more #example, and whatever main references. The template is optional rather than required, which is the part worth recording because it is a decision rather than an accident. Twenty-six fragments have none, and they divide cleanly: an area measure is defined by at least three control points the user places, so there is no fixed geometry a template could draw - that covers every area without one, Airhead Line included, which is an area despite the name - and the six Space Debris fragments are whole symbols in the way an ordinary icon is, so main holds all of it. Confirmed against both trees: 577 free canvas fragments each, 0 failing. Co-Authored-By: Claude Opus 5 --- .../src/main/resources/svg/README.md | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/jmsfx-standard/src/main/resources/svg/README.md b/jmsfx-standard/src/main/resources/svg/README.md index 39f096ef..26f123f2 100644 --- a/jmsfx-standard/src/main/resources/svg/README.md +++ b/jmsfx-standard/src/main/resources/svg/README.md @@ -47,7 +47,36 @@ For symbol assembly purposes, the following SIDC positions are used to determine ## Details -This section describes the details of the internal composition of the SVG files, for those developers who wish to make their own changes (TBD). +This section describes the details of the internal composition of the SVG files, for those developers who +wish to make their own changes. + +### Free canvas icons + +The Control Measure fragments, and three of the Cyberspace ones, are `FREE_CANVAS`: GIS construction samples +showing how a measure is drawn on a map, rather than icons composed into a symbol. APP-6E 8.1.3 exempts them +from the composition rules. They carry illustrative material alongside the drawn content, so each one holds: + +| element | how many | what it is | +| --- | --- | --- | +| `` | exactly one | the content - what a consumer renders | +| `` | zero or one | the construction guide: anchor point labels (`T`, `AS`) and the `PT 1`/`PT 2` arrows that place them | +| `` | zero or more | a worked sample, normally `display="none"`. Where there is more than one they are numbered `example1`, `example2` and so on | +| `` | as needed | anything `main` references. Patterns and markers belong here, never loose in the document or inside `main` | + +The template is optional because two kinds of fragment have nothing to construct: + +* An **area** is defined by at least three control points the user places, so there is no fixed geometry a + template could draw. That covers every area measure without one, including Airhead Line, which is an area + despite the name. +* The **Space Debris** fragments are whole symbols in the way an ordinary icon is, so `main` holds all of it. + +Nothing outside that list appears at the root of a free canvas fragment: no bare drawing elements, and no +groups under any other name. + +### Everything else + +Every other fragment holds a single content group, named for what it is - `frame`, `main`, `echelon`, `mod1`, +`mod2`. Those names are not yet consistent across the tree; making them so is tracked separately. ## Licensing From b452818ea961866c38313ec113b68f859f18b800 Mon Sep 17 00:00:00 2001 From: Colin Sharples Date: Fri, 25 Sep 2026 07:42:36 +1200 Subject: [PATCH 5/5] Fail the build when a free canvas fragment is off its shape (#78) Injecting fragments into the generated classes needs a rule for "which element is the content", and for the free canvas fragments that rule is the shape #78 settled: exactly one #main, zero or one #template, zero or more #example, and whatever main references. A fragment drifts off it easily - an editor leaves a group anonymous, a new icon arrives with its content loose at the root - and both of those had already happened. Catching it at verify costs nothing; catching it during injection means finding construction scaffolding rendered as though it were the drawing. FragmentShapeChecker reports every fault on a fragment at once, so a file is named once rather than once per thing wrong with it, and points at svg/README.md rather than restating the rule in an error message. Editor metadata is passed over deliberately: FragmentNormaliser's check is bound to the same phase and already fails on it with a message that says what to do, so reporting the same file twice for something that is not about its shape would only be noise. Which icons are free canvas, and which fragment each draws, moves into FreeCanvasIcons. FragmentMeasurer had that derivation inline, its own javadoc citing jmsfx#52 as the caution against duplicating it; a second copy here would have been exactly that mistake. The measurer now shares it and loses the two helpers that went with it. Behaviour-preserving: both models re-measure byte-identical. Bound alongside the normaliser check, taking the directory from the module layout for the same reason - config.yml's resourceDir is an absolute Windows path that resolves nowhere on a runner - and the model from the classpath, which does not have that problem. Verified it can actually fail: renaming one fragment's #main to #content fails the build with "25150100: a group called content; no ". 13 unit tests cover it, every case a shape that was genuinely in the tree before this issue. Co-Authored-By: Claude Opus 5 --- jmsfx-generator/pom.xml | 32 ++- .../jmsfx/generator/FragmentMeasurer.java | 82 +------ .../jmsfx/generator/FragmentShapeChecker.java | 231 ++++++++++++++++++ .../jmsfx/generator/FreeCanvasIcons.java | 113 +++++++++ .../generator/FragmentShapeCheckerTest.java | 119 +++++++++ 5 files changed, 505 insertions(+), 72 deletions(-) create mode 100644 jmsfx-generator/src/main/java/io/github/ctgnz/jmsfx/generator/FragmentShapeChecker.java create mode 100644 jmsfx-generator/src/main/java/io/github/ctgnz/jmsfx/generator/FreeCanvasIcons.java create mode 100644 jmsfx-generator/src/test/java/io/github/ctgnz/jmsfx/generator/FragmentShapeCheckerTest.java diff --git a/jmsfx-generator/pom.xml b/jmsfx-generator/pom.xml index 23f315a6..2cf3d4ce 100644 --- a/jmsfx-generator/pom.xml +++ b/jmsfx-generator/pom.xml @@ -115,9 +115,19 @@