ea 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (548) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -1
  3. data/Rakefile +3 -0
  4. data/TODO.complete/01-t-xref-parsing.md +91 -0
  5. data/TODO.complete/02-per-label-geometry.md +50 -0
  6. data/TODO.complete/03-object-properties-constraints.md +57 -0
  7. data/TODO.complete/04-operation-params.md +37 -0
  8. data/TODO.complete/05-pdata-flag-matrix.md +52 -0
  9. data/TODO.complete/06-emf-image-rendering.md +34 -0
  10. data/TODO.complete/07-auxiliary-tables.md +47 -0
  11. data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
  12. data/TODO.complete/11-gml-xsd-generation.md +42 -0
  13. data/TODO.complete/13-shapescript-interpreter.md +28 -0
  14. data/TODO.complete/20-cli-diff.md +46 -0
  15. data/TODO.complete/21-cli-render.md +26 -0
  16. data/TODO.complete/22-cli-export.md +29 -0
  17. data/TODO.complete/23-cli-mdg.md +23 -0
  18. data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
  19. data/TODO.complete/31-public-send-cleanup.md +61 -0
  20. data/TODO.complete/32-spec-coverage.md +33 -0
  21. data/TODO.complete/33-blocked-parity-gaps.md +41 -0
  22. data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
  23. data/TODO.complete/37-cli-lint.md +32 -0
  24. data/TODO.complete/38-cli-query.md +26 -0
  25. data/TODO.complete/39-cli-info.md +23 -0
  26. data/TODO.complete/40-ocl-evaluator.md +31 -0
  27. data/TODO.complete/41-round-trip-spec.md +18 -0
  28. data/TODO.complete/42-html-diff-report.md +18 -0
  29. data/TODO.complete/43-json-schema-export.md +20 -0
  30. data/TODO.complete/44-remaining-qea-tables.md +40 -0
  31. data/TODO.complete/45-shapescript-extensions.md +25 -0
  32. data/TODO.complete/46-xsd-consolidation.md +20 -0
  33. data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
  34. data/TODO.complete/51-remove-respond-to.md +25 -0
  35. data/TODO.complete/52-xsd-decouple-fixtures.md +23 -0
  36. data/TODO.complete/53-xmi-public-send-cleanup.md +26 -0
  37. data/TODO.complete/54-wire-stereotype-icon-renderer.md +20 -0
  38. data/TODO.complete/55-wire-shapescript.md +20 -0
  39. data/TODO.complete/56-wire-emf-renderer.md +27 -0
  40. data/TODO.complete/57-wire-ocl-evaluator.md +23 -0
  41. data/TODO.complete/58-diff-modifications.md +21 -0
  42. data/TODO.complete/59-delete-toy-xmi-export.md +30 -0
  43. data/TODO.complete/60-curate-json-export.md +20 -0
  44. data/TODO.complete/61-plantuml-relationships.md +22 -0
  45. data/TODO.complete/62-changelog.md +17 -0
  46. data/TODO.complete/63-dependabot-fix.md +18 -0
  47. data/TODO.complete/STATUS.md +85 -0
  48. data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
  49. data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
  50. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  51. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  52. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  53. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  54. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  55. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  56. data/TODO.diagrams/12-connector-labels.md +40 -0
  57. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  58. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  59. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  60. data/TODO.diagrams/14-center-header-text.md +33 -0
  61. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  62. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  63. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  64. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  65. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  66. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  67. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  68. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  69. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  70. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  71. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  72. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  73. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  74. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  75. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  76. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  77. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  78. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  79. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  80. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  81. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  82. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  83. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  84. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  85. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  86. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  87. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  88. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  89. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  90. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  91. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  92. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  93. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  94. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  95. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  96. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  97. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  98. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  99. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  100. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  101. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  102. data/TODO.diagrams/50-examples-parity.md +85 -0
  103. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  104. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  105. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  106. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  107. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  108. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  109. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  110. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  111. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  112. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  113. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  114. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  115. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  116. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  117. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  118. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  119. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  120. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  121. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  122. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  123. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  124. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  125. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  126. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  127. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  128. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  129. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  130. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  131. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  132. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  133. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  134. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  135. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  136. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  137. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  138. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  139. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  140. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  141. data/TODO.diagrams/README.md +69 -0
  142. data/TODO.diagrams/STATUS.md +73 -0
  143. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  144. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  145. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  146. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  147. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  148. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  149. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  150. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  151. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  152. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  153. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  154. data/TODO.fixup/12-path-style-harmony.md +23 -0
  155. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  156. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  157. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  158. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  159. data/TODO.fixup/17-path-count-calibration.md +27 -0
  160. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  161. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  162. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  163. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  164. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  165. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  166. data/TODO.fixup/24-note-element-rendering.md +23 -0
  167. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  168. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  169. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  170. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  171. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  172. data/TODO.fixup/30-theme-system-stub.md +46 -0
  173. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  174. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  175. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  176. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  177. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  178. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  179. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  180. data/TODO.fixup/38-note-element-rendering.md +15 -0
  181. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  182. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  183. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  184. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  185. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  186. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  187. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  188. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  189. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  190. data/TODO.fixup/48-background-rect-format.md +15 -0
  191. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  192. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  193. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  194. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  195. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  196. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  197. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  198. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  199. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  200. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  201. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  202. data/TODO.fixup/60-cli-theme-option.md +22 -0
  203. data/TODO.fixup/61-theme-validation.md +28 -0
  204. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  205. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  206. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  207. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  208. data/TODO.fixup/66-styleex-writeback.md +22 -0
  209. data/TODO.fixup/67-delete-dead-code.md +28 -0
  210. data/TODO.fixup/68-figure-parity-status.md +82 -0
  211. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  212. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  213. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  214. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  215. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  216. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  217. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  218. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  219. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  220. data/TODO.fixup/78-note-element-rendering.md +30 -0
  221. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  222. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  223. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  224. data/config/qea_schema.yml +356 -0
  225. data/config/themes/119.yml +98 -0
  226. data/config/themes/default.yml +14 -0
  227. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  228. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  229. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  230. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  231. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  232. data/examples/exports/UmlModel_template/model.xml +23 -0
  233. data/examples/exports/basic/UML_EA.DTD +6856 -0
  234. data/examples/exports/basic/model.xml +8039 -0
  235. data/examples/exports/simple/UML_EA.DTD +6856 -0
  236. data/examples/exports/simple/model.xml +585 -0
  237. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  238. data/examples/exports/simple_example/model.xml +23 -0
  239. data/examples/exports/test/UML_EA.DTD +6856 -0
  240. data/examples/exports/test/model.xml +1638 -0
  241. data/examples/qea/basic.qea +0 -0
  242. data/frontend/README.md +51 -0
  243. data/frontend/dist/app.iife.js +24 -0
  244. data/frontend/dist/style.css +1 -0
  245. data/frontend/index.html +26 -0
  246. data/frontend/package-lock.json +2359 -0
  247. data/frontend/package.json +26 -0
  248. data/frontend/src/App.vue +59 -0
  249. data/frontend/src/app.ts +8 -0
  250. data/frontend/src/components/AppHeader.vue +127 -0
  251. data/frontend/src/components/AppSidebar.vue +119 -0
  252. data/frontend/src/components/ClassDetails.vue +247 -0
  253. data/frontend/src/components/DiagramView.vue +79 -0
  254. data/frontend/src/components/PackageDetails.vue +92 -0
  255. data/frontend/src/components/PackageTreeNode.vue +64 -0
  256. data/frontend/src/components/SearchResults.vue +48 -0
  257. data/frontend/src/components/WelcomeView.vue +87 -0
  258. data/frontend/src/shims-vue.d.ts +5 -0
  259. data/frontend/src/stores/dataStore.ts +79 -0
  260. data/frontend/src/stores/uiStore.ts +167 -0
  261. data/frontend/src/styles/main.css +1566 -0
  262. data/frontend/src/types.ts +221 -0
  263. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  264. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  265. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  266. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  267. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  268. data/frontend/tests/e2e/render.spec.ts +188 -0
  269. data/frontend/tsconfig.json +20 -0
  270. data/frontend/vite.config.ts +26 -0
  271. data/frontend/vitest.config.ts +8 -0
  272. data/lib/ea/cli/app.rb +57 -2
  273. data/lib/ea/cli/command/diagrams.rb +37 -28
  274. data/lib/ea/cli/command/diff.rb +46 -0
  275. data/lib/ea/cli/command/export.rb +101 -0
  276. data/lib/ea/cli/command/info.rb +75 -0
  277. data/lib/ea/cli/command/lint.rb +48 -0
  278. data/lib/ea/cli/command/mdg.rb +98 -0
  279. data/lib/ea/cli/command/query.rb +34 -0
  280. data/lib/ea/cli/command/render.rb +135 -0
  281. data/lib/ea/cli/command/svg.rb +8 -15
  282. data/lib/ea/cli/command.rb +7 -0
  283. data/lib/ea/diagram/display_config.rb +184 -0
  284. data/lib/ea/diagram.rb +4 -41
  285. data/lib/ea/diff/change.rb +30 -0
  286. data/lib/ea/diff/comparator.rb +109 -0
  287. data/lib/ea/diff/html_reporter.rb +91 -0
  288. data/lib/ea/diff.rb +15 -0
  289. data/lib/ea/export/json/generator.rb +118 -0
  290. data/lib/ea/export/json.rb +12 -0
  291. data/lib/ea/export/json_schema/generator.rb +77 -0
  292. data/lib/ea/export/json_schema.rb +12 -0
  293. data/lib/ea/export/plantuml/generator.rb +140 -0
  294. data/lib/ea/export/plantuml.rb +12 -0
  295. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  296. data/lib/ea/export/xsd/generator.rb +137 -0
  297. data/lib/ea/export/xsd/namespace_registry.rb +82 -0
  298. data/lib/ea/export/xsd.rb +19 -0
  299. data/lib/ea/export.rb +13 -0
  300. data/lib/ea/image/emf_renderer.rb +42 -0
  301. data/lib/ea/image.rb +12 -0
  302. data/lib/ea/lint/engine.rb +30 -0
  303. data/lib/ea/lint/lint_rule.rb +38 -0
  304. data/lib/ea/lint/offense.rb +18 -0
  305. data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
  306. data/lib/ea/lint/rules/duplicate_name.rb +28 -0
  307. data/lib/ea/lint/rules/missing_stereotype.rb +40 -0
  308. data/lib/ea/lint/rules/naming_convention.rb +44 -0
  309. data/lib/ea/lint/rules/orphan_element.rb +28 -0
  310. data/lib/ea/lint/rules.rb +14 -0
  311. data/lib/ea/lint.rb +13 -0
  312. data/lib/ea/mdg/document.rb +120 -0
  313. data/lib/ea/mdg/loader.rb +118 -0
  314. data/lib/ea/mdg/registry.rb +98 -0
  315. data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
  316. data/lib/ea/mdg/xml/applies_to.rb +18 -0
  317. data/lib/ea/mdg/xml/apply.rb +18 -0
  318. data/lib/ea/mdg/xml/content.rb +18 -0
  319. data/lib/ea/mdg/xml/documentation.rb +24 -0
  320. data/lib/ea/mdg/xml/stereotype.rb +28 -0
  321. data/lib/ea/mdg/xml/stereotypes.rb +18 -0
  322. data/lib/ea/mdg/xml/tag.rb +28 -0
  323. data/lib/ea/mdg/xml/tagged_values.rb +18 -0
  324. data/lib/ea/mdg/xml/technology.rb +28 -0
  325. data/lib/ea/mdg/xml/uml_profile.rb +22 -0
  326. data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
  327. data/lib/ea/mdg/xml.rb +24 -0
  328. data/lib/ea/mdg.rb +25 -0
  329. data/lib/ea/model/classifier.rb +4 -0
  330. data/lib/ea/model/constraint.rb +24 -0
  331. data/lib/ea/model/diagram.rb +85 -0
  332. data/lib/ea/model/diagram_connector.rb +34 -0
  333. data/lib/ea/model/diagram_element.rb +16 -0
  334. data/lib/ea/model/document.rb +47 -2
  335. data/lib/ea/model/ghost_label.rb +32 -0
  336. data/lib/ea/model/instance_specification.rb +34 -0
  337. data/lib/ea/model/legend.rb +43 -0
  338. data/lib/ea/model/legend_item.rb +26 -0
  339. data/lib/ea/model/note.rb +28 -0
  340. data/lib/ea/model/operation.rb +2 -0
  341. data/lib/ea/model/property.rb +2 -0
  342. data/lib/ea/model/relationship.rb +2 -0
  343. data/lib/ea/model/signal.rb +14 -0
  344. data/lib/ea/model/slot.rb +19 -0
  345. data/lib/ea/model.rb +8 -0
  346. data/lib/ea/ocl/evaluator.rb +94 -0
  347. data/lib/ea/ocl/nodes.rb +30 -0
  348. data/lib/ea/ocl/parser.rb +133 -0
  349. data/lib/ea/ocl/unsupported_error.rb +7 -0
  350. data/lib/ea/ocl.rb +13 -0
  351. data/lib/ea/qea/factory/document_builder.rb +24 -10
  352. data/lib/ea/qea/factory/generalization_builder.rb +9 -11
  353. data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
  354. data/lib/ea/qea/models/ea_author.rb +35 -0
  355. data/lib/ea/qea/models/ea_glossary.rb +34 -0
  356. data/lib/ea/qea/models/ea_image.rb +53 -0
  357. data/lib/ea/qea/models/ea_implement.rb +32 -0
  358. data/lib/ea/qea/models/ea_list.rb +42 -0
  359. data/lib/ea/qea/models/ea_object_effort.rb +34 -0
  360. data/lib/ea/qea/models/ea_object_problem.rb +37 -0
  361. data/lib/ea/qea/models/ea_object_require.rb +34 -0
  362. data/lib/ea/qea/models/ea_object_resource.rb +34 -0
  363. data/lib/ea/qea/models/ea_object_risk.rb +34 -0
  364. data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
  365. data/lib/ea/qea/models/ea_object_test.rb +35 -0
  366. data/lib/ea/qea/models/ea_object_trx.rb +34 -0
  367. data/lib/ea/qea/models/ea_operation.rb +7 -0
  368. data/lib/ea/qea/models/ea_palette.rb +30 -0
  369. data/lib/ea/qea/models/ea_palette_item.rb +36 -0
  370. data/lib/ea/qea/models/ea_phase.rb +40 -0
  371. data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
  372. data/lib/ea/qea/models/ea_secrypt.rb +34 -0
  373. data/lib/ea/qea/models/ea_version.rb +39 -0
  374. data/lib/ea/qea/models/ea_xref.rb +11 -1
  375. data/lib/ea/qea/models.rb +19 -0
  376. data/lib/ea/qea/services/database_loader.rb +22 -1
  377. data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +65 -0
  378. data/lib/ea/qea/validation/database.rb +2 -0
  379. data/lib/ea/qea/validation/validation_engine.rb +2 -0
  380. data/lib/ea/qea/validation.rb +2 -0
  381. data/lib/ea/query/builder.rb +127 -0
  382. data/lib/ea/query.rb +9 -0
  383. data/lib/ea/render/image_converter.rb +101 -0
  384. data/lib/ea/render.rb +10 -0
  385. data/lib/ea/shapescript/parser.rb +208 -0
  386. data/lib/ea/shapescript/renderer.rb +74 -0
  387. data/lib/ea/shapescript/shape.rb +34 -0
  388. data/lib/ea/shapescript.rb +24 -0
  389. data/lib/ea/sources/qea/adapter.rb +22 -5
  390. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  391. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  392. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  393. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  394. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  395. data/lib/ea/sources/qea/note_builder.rb +58 -0
  396. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  397. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  398. data/lib/ea/sources/qea/package_builder.rb +16 -0
  399. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  400. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  401. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  402. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  403. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  404. data/lib/ea/sources/qea/xref/record.rb +41 -0
  405. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  406. data/lib/ea/sources/qea/xref.rb +27 -0
  407. data/lib/ea/sources/qea.rb +4 -0
  408. data/lib/ea/sources/xmi/adapter.rb +80 -2
  409. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  410. data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
  411. data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
  412. data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
  413. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  414. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  415. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  416. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  417. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  418. data/lib/ea/sources/xmi.rb +4 -0
  419. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  420. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  421. data/lib/ea/spa/output/strategy.rb +34 -0
  422. data/lib/ea/svg/connector_router.rb +134 -0
  423. data/lib/ea/svg/ea_emitter/background.rb +2 -3
  424. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  425. data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
  426. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  427. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  428. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  429. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  430. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  431. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  432. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  433. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  434. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  435. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  436. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  437. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  438. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  439. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  440. data/lib/ea/svg/ea_emitter/compartment/stereotype_icon.rb +32 -0
  441. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  442. data/lib/ea/svg/ea_emitter/compartment.rb +60 -0
  443. data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
  444. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  445. data/lib/ea/svg/ea_emitter/document.rb +49 -25
  446. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  447. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  448. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  449. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  450. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  451. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  452. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  453. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  454. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  455. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  456. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  457. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  458. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  459. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  460. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  461. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  462. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  463. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  464. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  465. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  466. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  467. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  468. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  469. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  470. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +119 -0
  471. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  472. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  473. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  474. data/lib/ea/svg/ea_emitter/elements.rb +283 -95
  475. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  476. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  477. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  478. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  479. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  480. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  481. data/lib/ea/svg/ea_emitter/labels.rb +93 -27
  482. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  483. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  484. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  485. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  486. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  487. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  488. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  489. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  490. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  491. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  492. data/lib/ea/svg/ea_emitter/markers.rb +351 -64
  493. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  494. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  495. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  496. data/lib/ea/svg/ea_emitter.rb +33 -11
  497. data/lib/ea/svg/parity/checker.rb +105 -0
  498. data/lib/ea/svg/parity/source.rb +59 -0
  499. data/lib/ea/svg/parity/suite.rb +113 -0
  500. data/lib/ea/svg/parity.rb +14 -0
  501. data/lib/ea/svg.rb +2 -15
  502. data/lib/ea/theme/definition.rb +176 -0
  503. data/lib/ea/theme/loader.rb +125 -0
  504. data/lib/ea/theme/registry.rb +96 -0
  505. data/lib/ea/theme.rb +37 -0
  506. data/lib/ea/transformations.rb +5 -88
  507. data/lib/ea/validation/xmi_parity.rb +80 -0
  508. data/lib/ea/validation.rb +12 -0
  509. data/lib/ea/version.rb +4 -1
  510. data/lib/ea.rb +15 -4
  511. data/lib/tasks/svg_bench.rake +87 -0
  512. data/lutaml-uml-0.5.1.gem +0 -0
  513. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  514. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  515. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  516. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  517. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  518. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  519. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  520. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  521. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  522. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  523. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  524. metadata +464 -26
  525. data/lib/ea/diagram/configuration.rb +0 -379
  526. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  527. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  528. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  529. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  530. data/lib/ea/diagram/element_renderers.rb +0 -43
  531. data/lib/ea/diagram/extractor.rb +0 -566
  532. data/lib/ea/diagram/layout_engine.rb +0 -170
  533. data/lib/ea/diagram/path_builder.rb +0 -202
  534. data/lib/ea/diagram/style_parser.rb +0 -42
  535. data/lib/ea/diagram/style_resolver.rb +0 -291
  536. data/lib/ea/diagram/svg_renderer.rb +0 -274
  537. data/lib/ea/diagram/util.rb +0 -73
  538. data/lib/ea/svg/bounds_calculator.rb +0 -74
  539. data/lib/ea/svg/connector_path.rb +0 -138
  540. data/lib/ea/svg/element_box.rb +0 -256
  541. data/lib/ea/svg/renderer.rb +0 -77
  542. data/lib/ea/svg/style_resolver.rb +0 -72
  543. data/lib/ea/transformations/configuration.rb +0 -333
  544. data/lib/ea/transformations/format_registry.rb +0 -366
  545. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  546. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  547. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  548. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -0,0 +1,27 @@
1
+ # 19 - Connector Source/Target Edge Routing from SX/SY/EX/EY
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ `ConnectorRouter` now accepts `source_delta` and `target_delta`
8
+ (defaulting to [0,0]). `DiagramBuilder#compute_waypoints` passes
9
+ SX/SY/EX/EY from the parsed geometry to the router, which applies
10
+ them as deltas to the edge-attachment point.
11
+
12
+ `ExtensionGeometryParser.Placement` already exposed `sx`, `sy`,
13
+ `ex`, `ey` — just plumbs them through.
14
+
15
+ ## Remaining diff
16
+
17
+ Waterway connectors still show ~20 px y-offset vs EA reference
18
+ (643 vs 663). The SX/SY deltas are small (-21 to 150) and don't
19
+ account for this gap. EA likely uses a different attachment
20
+ formula (not strict edge center) — possibly offset by half
21
+ font-height or other rendering consideration. Deferred to deeper
22
+ reverse-engineering.
23
+
24
+ ## Files changed
25
+
26
+ - `lib/ea/svg/connector_router.rb` — source_delta / target_delta support
27
+ - `lib/ea/sources/xmi/diagram_builder.rb` — passes deltas to router
@@ -0,0 +1,31 @@
1
+ # TODO-D 19: Replace respond_to? with is_a? (encapsulation)
2
+
3
+ ## Problem
4
+
5
+ `Ea::Svg::EaEmitter::Labels#connector_stereotype` uses
6
+ `respond_to?(:stereotype)`:
7
+
8
+ ```ruby
9
+ def connector_stereotype(connector)
10
+ rel = model_index ? model_index[connector.relationship_ref] : nil
11
+ stereotype = rel&.respond_to?(:stereotype) ? rel.stereotype : nil
12
+ return nil if stereotype.nil? || stereotype.to_s.empty?
13
+ "«#{stereotype}»"
14
+ end
15
+ ```
16
+
17
+ `respond_to?` is duck-typing that hides type errors until runtime.
18
+ Per the project rules: use `is_a?` for type checks, or design the
19
+ type hierarchy so the check isn't needed.
20
+
21
+ ## Fix
22
+
23
+ `Ea::Model::Relationship` (base class) now declares `stereotype` as
24
+ an attribute. All concrete relationship subclasses inherit it. So
25
+ the check should be `rel.is_a?(Ea::Model::Relationship)`.
26
+
27
+ ## Acceptance
28
+
29
+ - No `respond_to?` calls in `lib/`.
30
+ - Connector stereotype labels still render correctly.
31
+ - Specs pass.
@@ -0,0 +1,56 @@
1
+ # TODO-D 20: Split Labels emitter into MECE collaborators
2
+
3
+ ## Problem
4
+
5
+ `Ea::Svg::EaEmitter::Labels` currently handles three distinct
6
+ responsibilities:
7
+
8
+ 1. Connector midpoint stereotype labels (e.g. «import»).
9
+ 2. End-label role + «property» + multiplicity rendering.
10
+ 3. Geometry box parsing and position resolution.
11
+
12
+ These change for different reasons:
13
+
14
+ - Midpoint rendering changes when EA's connector stereotype
15
+ conventions change.
16
+ - End-label rendering changes when role-name / multiplicity
17
+ rules change.
18
+ - Box parsing changes when the QEA geometry format changes.
19
+
20
+ MECE violation: the three concerns overlap inside one class. OCP
21
+ violation: adding a new label kind (e.g. connector name above the
22
+ midpoint) requires editing Labels rather than registering a new
23
+ collaborator.
24
+
25
+ ## Fix
26
+
27
+ Introduce a small LabelRenderer registry:
28
+
29
+ ```
30
+ Ea::Svg::EaEmitter::Label
31
+ ├─ EndLabel (role + «property» + mult at LLT/LRT boxes)
32
+ ├─ MidpointLabel (stereotype at path midpoint)
33
+ └─ Registry (dispatch by relationship kind / connector flag)
34
+ ```
35
+
36
+ `Labels#texts_for` becomes:
37
+
38
+ ```ruby
39
+ def texts_for(connector)
40
+ points = waypoint_pairs(connector)
41
+ return [] if points.size < 2
42
+
43
+ Label::Registry.for(connector, model_index: model_index)
44
+ .flat_map { |r| r.texts(connector, points) }
45
+ end
46
+ ```
47
+
48
+ Each renderer is independently testable. New label kinds are added
49
+ by registering, not by editing Labels (OCP).
50
+
51
+ ## Acceptance
52
+
53
+ - Three discrete classes; each has its own spec.
54
+ - No behavior change (parity text overlap ≥ 87%).
55
+ - Existing labels spec still passes.
56
+ - New midpoint label spec covers the «import» path.
@@ -0,0 +1,27 @@
1
+ # 20 - Visibility Glyph Font (Plus Sign in Monospace)
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ Three text-encoding fixes:
8
+ 1. Visibility marker now emits as `+ ` (with trailing space) —
9
+ matches EA's text content exactly.
10
+ 2. Namespace separator in type names converted to `::` (UML
11
+ standard) — `gml:CodeType` → `gml::CodeType`.
12
+ 3. Abstract class names no longer get double underscore when the
13
+ source name already starts with `_` (e.g. `_Feature` stays
14
+ `_Feature`, not `__Feature`).
15
+
16
+ ## Remaining gap
17
+
18
+ Text counts overshoot ref on diagrams where the source classifier
19
+ has more `ownedAttribute` entries than EA displays. EA truncates the
20
+ visible attribute list (likely a per-diagram display filter or box-
21
+ size fit), while we render every property. This is a content-
22
+ filtering concern, not an encoding concern — deferred.
23
+
24
+ ## Files changed
25
+
26
+ - `lib/ea/svg/ea_emitter/elements.rb` — `abstract_name`,
27
+ `namespace_double_colon`, `split_visibility` updates
@@ -0,0 +1,21 @@
1
+ # 21 - Aggregation Navigability Arrow at Part End
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ `Markers#marker_specs` for Aggregation/Composition now returns TWO
8
+ Specs:
9
+ - Diamond polygon at whole end
10
+ - Arrow path at part end
11
+
12
+ Document interleaves line + diamond + arrow per aggregation
13
+ connector (via `count_for` API + `pair_per_connector`).
14
+
15
+ Waterway path count: 15 → 21 (ref=20, within ±1).
16
+ Bridge polygon+path counts now within ±2 of ref.
17
+
18
+ ## Files changed
19
+
20
+ - `lib/ea/svg/ea_emitter/markers.rb` — `marker_specs` returns Array, `count_for` API
21
+ - `lib/ea/svg/ea_emitter/document.rb` — `pair_per_connector` interleave
@@ -0,0 +1,65 @@
1
+ # TODO-D 21: Auto-generated legend block
2
+
3
+ ## Problem
4
+
5
+ EA renders a "凡例" (Legend) block in many diagrams that contain
6
+ color-coded stereotype elements. The block contains:
7
+
8
+ - A rounded-corner container rect (`rx=3`)
9
+ - 3 color swatch rects (`17×17`)
10
+ - A bold 12pt #003060 title "凡例"
11
+ - Three 9pt #003060 lines naming the source packages
12
+
13
+ Reference SVG example (EAID_0016F797):
14
+
15
+ ```xml
16
+ <rect x="485" y="368" width="194" height="120" rx="3.00"/>
17
+ <rect x="495" y="398" width="17" height="17"/> <!-- swatch 1 -->
18
+ <rect x="495" y="417" width="17" height="17"/> <!-- swatch 2 -->
19
+ <rect x="495" y="436" width="17" height="17"/> <!-- swatch 3 -->
20
+ <text x="546.00" y="387.00" font-weight:700 font-size:12pt>凡例</text>
21
+ <text x="517.00" y="410.00">GMLに定義されたクラス</text>
22
+ <text x="517.00" y="429.00">CityGMLに定義されたクラス</text>
23
+ <text x="517.00" y="448.00">i-URに定義されたクラス</text>
24
+ ```
25
+
26
+ The block is NOT modeled in the QEA schema. There is no "Legend"
27
+ Object_Type, no t_lists row, no StyleEx flag tied to it.
28
+
29
+ ## Investigation Notes
30
+
31
+ - 27 Note objects in the QEA, none contains "凡例".
32
+ - 5 Text objects in the QEA, all empty Note field.
33
+ - The legend titles and swatch colors vary per diagram but follow
34
+ a fixed vocabulary: GML, CityGML, i-UR.
35
+ - The color swatches are the stereotype fill colors of elements
36
+ visible in the diagram (e.g. FFCFFF for CityGML, CCFFCC for GML).
37
+
38
+ ## Hypothesis
39
+
40
+ EA's "Diagram Legend" feature is a built-in diagram decoration
41
+ that auto-generates when:
42
+
43
+ 1. The diagram contains elements whose stereotype maps to a
44
+ known "package source" (GML, CityGML, i-UR).
45
+ 2. The user has placed a Legend UI element (which EA serializes
46
+ somewhere we haven't found yet — possibly t_image or a binary
47
+ blob).
48
+
49
+ Without reverse-engineering EA.exe's legend code path or finding
50
+ the serialization, replicating this verbatim is high-risk.
51
+
52
+ ## Status
53
+
54
+ Deferred. Pinned in the parity report (TODO.diagrams/
55
+ 17-remaining-parity-gaps.md) as a known ~448-text gap.
56
+
57
+ ## Acceptance (when picked up)
58
+
59
+ - Locate the legend serialization in the QEA (table + column).
60
+ - Model `Ea::Model::Legend` with `position`, `title`, `entries`
61
+ (each entry: color + label).
62
+ - Build `Ea::Svg::EaEmitter::Legend` renderer producing the
63
+ container rect + swatch rects + texts.
64
+ - Specs cover: empty legend, single-entry, three-entry.
65
+ - Parity text overlap climbs above 90%.
@@ -0,0 +1,57 @@
1
+ # TODO-D 22: Ghost element discriminator
2
+
3
+ ## Problem
4
+
5
+ EA renders some elements as "ghosts": smaller font (7pt), gray
6
+ text (#595959), no attribute/operation/tag compartments. The
7
+ discriminator is unclear.
8
+
9
+ ## Empirical Observations
10
+
11
+ | Diagram | Element | ObjectStyle | Rendered as |
12
+ |--------------------|----------------------------|------------------------------------------------------|-------------|
13
+ | 位置図C0301 | _UrbanFunction | NSL=0;LCol=-1;...font=Yu Gothic UI;... | Full |
14
+ | 位置図C0301 | MultiSurface | NSL=0;BFol=-1;...fontsz=0;bold=0;... (no font=) | Ghost |
15
+ | CityGMLCore | _Feature | NSL=0;BCol=-1;BFol=-1;LCol=-1;LWth=-1;fontsz=0;... | Ghost |
16
+ | urf_4 | _UrbanFunction | NSL=0;BFol=-1;LCol=-1;fontsz=0;bold=0;... (no font=) | **Full** |
17
+ | 都市計画決定情報 | _UrbanFunction | NSL=0;LCol=-1;bold=0;... (no font=) | Ghost |
18
+
19
+ The presence or absence of `font=` is **not** the discriminator
20
+ (urf_4 elements lack `font=` but render full attributes).
21
+
22
+ `BCol` value is **not** the discriminator (urf_4 has explicit
23
+ BCol=16777215; MultiSurface in 位置図C0301 has BCol=13434828).
24
+
25
+ Classifier `package_id` vs diagram `package_id` is **not** the
26
+ discriminator (urf_4 cross-package elements get full attrs;
27
+ CityGMLCore cross-package elements get ghost).
28
+
29
+ ## Hypothesis To Test
30
+
31
+ 1. **Diagram-level "is reference" flag**: maybe EA marks diagrams
32
+ that only show snapshots of an external model. The discriminator
33
+ could be `t_diagram.SPT=1` (already 1 in all observed diagrams)
34
+ combined with another flag.
35
+
36
+ 2. **Element-level "Link" semantics**: maybe elements placed as
37
+ "links" rather than embedded get ghost styling. The QEA may
38
+ encode this in t_diagramlinks.Path or t_object.Documentation
39
+ style flags we haven't parsed.
40
+
41
+ 3. **t_objectstatistics or t_objectproperties**: an explicit "ghost
42
+ mode" tagged value.
43
+
44
+ ## Status
45
+
46
+ Deferred. Without EA.exe disassembly or empirical testing inside
47
+ the EA GUI, the rule cannot be confidently derived.
48
+
49
+ ## Acceptance (when picked up)
50
+
51
+ - Open the QEA in EA GUI, toggle the "Link" vs "Embed" property
52
+ on an element, observe the rendered SVG, and identify the QEA
53
+ field that changes.
54
+ - Implement a `ghost?` predicate on `Ea::Model::DiagramElement`
55
+ using the discovered field.
56
+ - Wire through `Elements` emitter to apply ghost styling.
57
+ - Specs cover both ghost and full paths.
@@ -0,0 +1,28 @@
1
+ # 22 - Style-Based Path Grouping
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ `Connectors` emitter now supports `group_by_style:` constructor
8
+ option. When true, consecutive connectors with the same line style
9
+ are merged into a single `<g>` element with multiple `<path>` kids
10
+ (matching EA's encoding for diagrams with many similarly-styled
11
+ connectors).
12
+
13
+ Default behavior remains per-entity grouping (one `<g>` per
14
+ connector) to preserve z-order interleaving with markers.
15
+
16
+ ## Usage
17
+
18
+ ```ruby
19
+ # Default: per-entity groups (current behavior)
20
+ Connectors.new(diagram, canvas: canvas)
21
+
22
+ # EA-style grouped:
23
+ Connectors.new(diagram, canvas: canvas, group_by_style: true)
24
+ ```
25
+
26
+ ## Files changed
27
+
28
+ - `lib/ea/svg/ea_emitter/connectors.rb` — `group_by_style` option + `grouped_groups`
@@ -0,0 +1,47 @@
1
+ # TODO-D 23: Marker polygon deduplication rules
2
+
3
+ ## Problem
4
+
5
+ EA's rules for emitting polygon markers when multiple connectors
6
+ share an endpoint are non-obvious.
7
+
8
+ ## Empirical Observations
9
+
10
+ Aggregate across 188 reference SVGs:
11
+ - Total polygons: 1485
12
+ - Exact-duplicate polygons (same points string): **536** (36%)
13
+
14
+ EA duplicates polygon markers in many cases but not always. The
15
+ discriminator is unclear.
16
+
17
+ ## Experiments Tried
18
+
19
+ | Strategy | Polygon delta | Within-tol |
20
+ |-----------------------------|---------------|------------|
21
+ | Anchor-based dedup (current)| -166 (-11.2%) | 123/188 (65.4%) |
22
+ | No dedup | +257 (+17.3%) | 139/188 (73.9%) |
23
+ | Exact-body dedup | +247 (+16.6%) | 141/188 (75.0%) |
24
+
25
+ Both "no dedup" and "exact-body dedup" improve within-tolerance
26
+ diagram count (+13%) but introduce polygon overshoot. Anchor-based
27
+ dedup has best polygon delta but lower diagram-tol rate.
28
+
29
+ ## Hypothesis To Test
30
+
31
+ EA likely has per-marker-per-connector emission for `Path=`-routed
32
+ connectors (each visible link emits its own markers) but deduplicates
33
+ markers that share an anchor AND belong to connectors sharing a
34
+ common segment.
35
+
36
+ The QEA stores a `Path=` field on t_diagramlinks; connectors that
37
+ share the same visual path are co-rendered. We currently treat
38
+ each t_diagramlinks row independently.
39
+
40
+ ## Acceptance (when picked up)
41
+
42
+ - Correlate polygon duplicates with shared `Path=` values.
43
+ - Implement per-diagram `grouped:` mode selection in Markers.
44
+ - Specs cover: distinct anchors (no dedup), shared anchor + shared
45
+ path (dedup), shared anchor + distinct path (no dedup).
46
+ - Polygon delta within ±20 (currently -166).
47
+ - Within-tolerance diagrams ≥ 75% (currently 65.4%).
@@ -0,0 +1,29 @@
1
+ # 23 - Diagram-Level Multiplicity Label Positioning
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ Refactored `Ea::Svg::EaEmitter::Labels` to emit per-end role name +
8
+ multiplicity using LLB (left label box) / LLT (left label text) and
9
+ RLB / RLT offsets from the connector geometry.
10
+
11
+ Uses UML convention: the role visible at one end of the connector is
12
+ the property owned by the OPPOSITE classifier. So LLT (near source)
13
+ shows the target-end property; RLT (near target) shows the source-
14
+ end property.
15
+
16
+ Each label emits as TWO `<text>` elements matching EA's encoding:
17
+ one for the role name (`+roleName`) and one for the multiplicity
18
+ (`0..1`).
19
+
20
+ ## Files changed
21
+
22
+ - `lib/ea/svg/ea_emitter/labels.rb` — full rewrite with model_index,
23
+ association_ends, label_position, role_text, multiplicity_text
24
+ - `lib/ea/svg/ea_emitter/document.rb` — passes `model_index` to Labels
25
+
26
+ ## Outcome metrics
27
+
28
+ Text count mean abs diff dropped from 20 → 14 across 185 diagrams.
29
+ Waterway: 92 → 103 (ref=113). CityFurniture: 24 → 30 (ref=34).
@@ -0,0 +1,35 @@
1
+ # TODO-D 24: Track marker anchors structurally
2
+
3
+ ## Problem
4
+
5
+ `Ea::Svg::EaEmitter::Markers#anchor_key` parses the SVG string the
6
+ class itself just emitted in order to deduplicate entries by their
7
+ anchor point:
8
+
9
+ ```ruby
10
+ def anchor_key(body)
11
+ match = body.match(/polygon points="(-?[\d.]+)\s+(-?[\d.]+)|path d="M (-?[\d.]+)\s+(-?[\d.]+)/)
12
+ return body unless match
13
+ ...
14
+ end
15
+ ```
16
+
17
+ This is fragile:
18
+
19
+ - Round-trip through string parsing for data we already had
20
+ structurally.
21
+ - Regex assumes EA's exact SVG attribute ordering and whitespace.
22
+ - Hard to test in isolation (must build the body string first).
23
+
24
+ ## Fix
25
+
26
+ Add `anchor` to the `Entry` struct. Capture it when the entry is
27
+ built (we already have `tip` at that point). Dedup on
28
+ `Entry#anchor` rather than re-parsing the body.
29
+
30
+ ## Acceptance
31
+
32
+ - `Entry` struct carries `[x, y]` anchor.
33
+ - `anchor_key` method removed; dedup uses `Entry#anchor` directly.
34
+ - Markers specs continue to pass.
35
+ - Parity polygon delta unchanged.
@@ -0,0 +1,66 @@
1
+ # TODO-D 24: Track marker anchors structurally (DONE)
2
+
3
+ ## Before
4
+
5
+ `Markers#anchor_key` parsed the rendered SVG string with regex to
6
+ extract the marker's anchor coordinates:
7
+
8
+ ```ruby
9
+ def anchor_key(body)
10
+ match = body.match(/polygon points="(-?[\d.]+)\s+(-?[\d.]+)|path d="M (-?[\d.]+)\s+(-?[\d.]+)/)
11
+ return body unless match
12
+ x = (match[1] || match[3]).to_f.round
13
+ y = (match[2] || match[4]).to_f.round
14
+ [x, y]
15
+ end
16
+ ```
17
+
18
+ That mixes "data" (the anchor) with "presentation" (the rendered
19
+ string), and it parses the same SVG twice. Re-introducing the
20
+ anchor as a Ruby value fixes both.
21
+
22
+ ## After
23
+
24
+ `Markers::Entry` is now a Struct that carries the anchor alongside
25
+ the body, with hash/eql? defined so `Array#uniq` collapses by
26
+ anchor without touching the SVG:
27
+
28
+ ```ruby
29
+ Entry = Struct.new(:style_key, :body, :anchor, keyword_init: true) do
30
+ def hash
31
+ anchor.hash
32
+ end
33
+
34
+ def eql?(other)
35
+ other.is_a?(Entry) && anchor == other.anchor
36
+ end
37
+ end
38
+ ```
39
+
40
+ `entries_for` computes the anchor once when building the Entry:
41
+
42
+ ```ruby
43
+ def anchor_for(spec)
44
+ tx, ty = translate_point(spec.anchor)
45
+ [tx.round, ty.round]
46
+ end
47
+ ```
48
+
49
+ ## Benefits
50
+
51
+ - **Performance**: one anchor computation per marker instead of
52
+ a regex match per dedup pass.
53
+ - **Type safety**: anchor is a `[Integer, Integer]` pair, not a
54
+ parsed fragment of a String.
55
+ - **Encapsulation**: rendered SVG never re-parsed.
56
+ - **Testability**: `Entry#eql?` is unit-testable in isolation.
57
+
58
+ ## Tests
59
+
60
+ - `spec/ea/svg/ea_emitter/markers_entry_spec.rb` covers anchor
61
+ equality, deduplication, and false-positive guards.
62
+
63
+ ## Bench
64
+
65
+ Unchanged: rect -221, path -280, polygon -6, text -601,
66
+ overlap 64.5%. The refactor is behavior-preserving.
@@ -0,0 +1,24 @@
1
+ # 24 - Truncate Properties to Fit Box (Match EA Display)
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ Discovered the actual rule: EA hides properties that are navigable
8
+ ends of an association (those are rendered as connector lines, not
9
+ as attribute text). This is NOT box-fit truncation — it's a content
10
+ filter based on property semantics.
11
+
12
+ ## Implementation
13
+
14
+ 1. Added `association_id` field to `Ea::Model::Property` (non-nil
15
+ when the property is a navigable end of an Association).
16
+ 2. `Ea::Sources::Xmi::PropertyBuilder` captures `attr.association`.
17
+ 3. `Ea::Svg::EaEmitter::Elements#displayable_properties` rejects
18
+ properties with `association_id` set.
19
+
20
+ ## Files changed
21
+
22
+ - `lib/ea/model/property.rb` — `association_id` attribute + JSON mapping
23
+ - `lib/ea/sources/xmi/property_builder.rb` — populate `association_id`
24
+ - `lib/ea/svg/ea_emitter/elements.rb` — `displayable_properties` filter
@@ -0,0 +1,59 @@
1
+ # TODO-D 26: Phantom general-parent rendering
2
+
3
+ ## Problem
4
+
5
+ The reference SVGs contain class headers (`_AbstractGeometricAggregate`,
6
+ `_CityObject`, `_FeatureCollection`, etc.) that do NOT appear in
7
+ `t_diagramobjects` for the diagram. ~150 missing text instances
8
+ across the plateau dataset.
9
+
10
+ ## Empirical Rule (partial)
11
+
12
+ When a class is placed in a diagram, EA also displays its
13
+ generalization parent if the parent isn't already in the diagram.
14
+
15
+ Validation across 188 diagrams:
16
+
17
+ - Total parent-checks: 241
18
+ - Parent rendered in SVG: 129 (53.5%)
19
+
20
+ 53.5% match suggests the rule is "right idea, wrong details." Other
21
+ factors:
22
+
23
+ - Per-element font/style override may suppress phantom rendering
24
+ when the parent's style isn't explicit
25
+ - The discriminator may be tied to AttPkg / SuppressFOC flags
26
+ - EA may only render one level up the generalization chain
27
+
28
+ ## Implementation Sketch
29
+
30
+ Add `phantom_generalizations_for(diagram, model_index)` returning
31
+ phantom DiagramElements with synthetic bounds (placed near their
32
+ child). Render them through the existing `groups_for` path so they
33
+ participate in the same compartment math, color resolution, etc.
34
+
35
+ ```ruby
36
+ def ordered_elements
37
+ placed = (diagram.elements || []).sort_by { |e| e.z_order || 0 }
38
+ placed + phantom_elements(placed)
39
+ end
40
+
41
+ def phantom_elements(placed)
42
+ placed_ids = placed.map(&:model_element_ref)
43
+ parents = placed_ids.flat_map { |id| generalizations_of(id) }.uniq - placed_ids
44
+ parents.map { |pid| phantom_diagram_element(pid) }
45
+ end
46
+ ```
47
+
48
+ The hard part is positioning: EA computes phantoms at coordinates
49
+ we cannot derive without GUI testing or sample-data correlation.
50
+
51
+ ## Acceptance
52
+
53
+ - Render general-parent phantoms for at least the cases where the
54
+ rule applies cleanly.
55
+ - Phantom bounding box is plausible (within canvas extents).
56
+ - Parity text overlap climbs to ~90%+.
57
+ - Specs cover: parent not in diagram (phantom added), parent
58
+ already in diagram (no duplicate), phantom appears in correct
59
+ sub-layer.
@@ -0,0 +1,43 @@
1
+ # TODO-D 27: Per-diagram path grouping mode detection
2
+
3
+ ## Problem
4
+
5
+ EA emits connector paths in two styles:
6
+
7
+ 1. **Grouped** — All same-style paths joined into one `<g>` block
8
+ (matches what we currently do in `LayerSequencer#merge_layers_by_style`).
9
+ 2. **Per-connector** — Each connector path in its own `<g>` block.
10
+
11
+ Some diagrams use one mode, others use the other. We always use
12
+ grouped, contributing to the -181 path delta.
13
+
14
+ ## Investigation
15
+
16
+ Tested in StyleEx flags — no obvious "grouped" toggle. EA's
17
+ emit-mode decision appears to be runtime/export-time, not stored in
18
+ the QEA.
19
+
20
+ ## Empirical Observations
21
+
22
+ - Grouped-style diagrams: 0 to 5 path-delta diagrams (most match)
23
+ - Per-connector-style diagrams: 10-30 path-delta (we're missing 10-30
24
+ `<g>` wrappers)
25
+
26
+ The discriminator might be:
27
+
28
+ - Diagram export-time option set in EA GUI at export
29
+ - Per-diagram count threshold (more connectors → per-connector)
30
+ - User preference cached in some EA-specific location we can't access
31
+
32
+ ## Status
33
+
34
+ Deferred. Without empirical access to EA's export preferences or a
35
+ binary blob in the QEA, the mode detection is speculative.
36
+
37
+ ## Acceptance (when picked up)
38
+
39
+ - Identify the QEA field or runtime condition that controls grouping.
40
+ - Implement a per-diagram detector.
41
+ - Wire into LayerSequencer: `grouped: detect_grouped_mode(diagram)`.
42
+ - Specs cover: small diagrams → grouped, large diagrams → per-connector.
43
+ - Path delta within ±20 of zero.
@@ -0,0 +1,47 @@
1
+ # TODO-D 28-31: Architectural cleanup batch
2
+
3
+ ## Completed (this session)
4
+
5
+ ### 28. Canvas promoted to proper class
6
+
7
+ `lib/ea/svg/ea_emitter/canvas.rb` was a `Struct.new + do ... end`
8
+ block. Promoted to an explicit class with `attr_reader` for the
9
+ four bound fields. Public API is now named rather than implicit;
10
+ the type stays immutable.
11
+
12
+ 8 new specs cover `translate_x`, `translate_y`, `.coord`,
13
+ immutability, and the existing `.from` factory.
14
+
15
+ ### 29. OpenStruct removed
16
+
17
+ `elements.rb#translate_bounds` returned an `OpenStruct.new`. This
18
+ required `require "ostruct"` at the top of the file and obscured
19
+ the return type.
20
+
21
+ Now returns `Ea::Model::Bounds.new` directly — the existing model
22
+ value object — and the `require "ostruct"` line is removed. Type
23
+ flows cleanly through the pipeline (model → renderer).
24
+
25
+ ### 31. Specs for under-tested components
26
+
27
+ - `spec/ea/svg/ea_emitter/canvas_spec.rb` — added `translate_x`,
28
+ `translate_y`, `.coord`, and immutability specs.
29
+ - `spec/ea/svg/ea_emitter/layer_spec.rb` — new file (10 specs).
30
+ - `spec/ea/svg/ea_emitter/background_spec.rb` — new file (3 specs).
31
+
32
+ ## Deferred
33
+
34
+ ### 30. Decompose Elements groups_for
35
+
36
+ `Elements#groups_for` is ~100 lines of linear rendering steps.
37
+ Two previous attempts to extract a helper method broke 5+ specs
38
+ due to complex parameter threading (model_element vs classifier
39
+ distinction, Optional bounds, theme plumbing).
40
+
41
+ The method reads as a recipe: each step adds one output string.
42
+ Linear recipes are easier to read as one method than as a chain
43
+ of helper calls with overlapping parameter lists.
44
+
45
+ Deferred until a structural refactor (e.g. a Compartments
46
+ collaborator that holds rendering state) is justified by a new
47
+ requirement.