ea 0.3.1 → 0.5.0

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 (544) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -0
  3. data/TODO.complete/01-t-xref-parsing.md +91 -0
  4. data/TODO.complete/02-per-label-geometry.md +50 -0
  5. data/TODO.complete/03-object-properties-constraints.md +57 -0
  6. data/TODO.complete/04-operation-params.md +37 -0
  7. data/TODO.complete/05-pdata-flag-matrix.md +52 -0
  8. data/TODO.complete/06-emf-image-rendering.md +34 -0
  9. data/TODO.complete/07-auxiliary-tables.md +47 -0
  10. data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
  11. data/TODO.complete/11-gml-xsd-generation.md +42 -0
  12. data/TODO.complete/13-shapescript-interpreter.md +28 -0
  13. data/TODO.complete/20-cli-diff.md +46 -0
  14. data/TODO.complete/21-cli-render.md +26 -0
  15. data/TODO.complete/22-cli-export.md +29 -0
  16. data/TODO.complete/23-cli-mdg.md +23 -0
  17. data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
  18. data/TODO.complete/31-public-send-cleanup.md +61 -0
  19. data/TODO.complete/32-spec-coverage.md +33 -0
  20. data/TODO.complete/33-blocked-parity-gaps.md +41 -0
  21. data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
  22. data/TODO.complete/37-cli-lint.md +32 -0
  23. data/TODO.complete/38-cli-query.md +26 -0
  24. data/TODO.complete/39-cli-info.md +23 -0
  25. data/TODO.complete/40-ocl-evaluator.md +31 -0
  26. data/TODO.complete/41-round-trip-spec.md +18 -0
  27. data/TODO.complete/42-html-diff-report.md +18 -0
  28. data/TODO.complete/43-json-schema-export.md +20 -0
  29. data/TODO.complete/44-remaining-qea-tables.md +40 -0
  30. data/TODO.complete/45-shapescript-extensions.md +25 -0
  31. data/TODO.complete/46-xsd-consolidation.md +20 -0
  32. data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
  33. data/TODO.complete/STATUS.md +109 -0
  34. data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
  35. data/TODO.diagrams/01-xmi-extension-elements-parsing.md +67 -0
  36. data/TODO.diagrams/02-diagram-element-rich-model.md +61 -0
  37. data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
  38. data/TODO.diagrams/03-ea-style-svg-emitter.md +70 -0
  39. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  40. data/TODO.diagrams/04-connector-routing-from-soid-eoid.md +55 -0
  41. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  42. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  43. data/TODO.diagrams/05-marker-rendering-per-relationship-type.md +49 -0
  44. data/TODO.diagrams/06-connector-labels.md +49 -0
  45. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  46. data/TODO.diagrams/07-canvas-size-and-viewbox.md +39 -0
  47. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  48. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  49. data/TODO.diagrams/08-visual-regression-harness.md +34 -0
  50. data/TODO.diagrams/09-cli-render-all-diagrams.md +25 -0
  51. data/TODO.diagrams/10-stereotype-color-config.md +29 -0
  52. data/TODO.diagrams/11-elkrb-auto-layout-integration.md +53 -0
  53. data/TODO.diagrams/12-connector-labels.md +40 -0
  54. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  55. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  56. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  57. data/TODO.diagrams/14-center-header-text.md +33 -0
  58. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  59. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  60. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  61. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  62. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  63. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  64. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  65. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  66. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  67. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  68. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  69. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  70. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  71. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  72. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  73. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  74. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  75. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  76. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  77. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  78. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  79. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  80. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  81. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  82. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  83. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  84. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  85. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  86. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  87. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  88. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  89. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  90. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  91. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  92. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  93. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  94. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  95. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  96. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  97. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  98. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  99. data/TODO.diagrams/50-examples-parity.md +85 -0
  100. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  101. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  102. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  103. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  104. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  105. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  106. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  107. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  108. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  109. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  110. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  111. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  112. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  113. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  114. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  115. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  116. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  117. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  118. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  119. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  120. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  121. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  122. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  123. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  124. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  125. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  126. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  127. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  128. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  129. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  130. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  131. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  132. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  133. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  134. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  135. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  136. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  137. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  138. data/TODO.diagrams/README.md +69 -0
  139. data/TODO.diagrams/STATUS.md +73 -0
  140. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  141. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  142. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  143. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  144. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  145. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  146. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  147. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  148. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  149. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  150. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  151. data/TODO.fixup/12-path-style-harmony.md +23 -0
  152. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  153. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  154. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  155. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  156. data/TODO.fixup/17-path-count-calibration.md +27 -0
  157. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  158. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  159. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  160. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  161. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  162. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  163. data/TODO.fixup/24-note-element-rendering.md +23 -0
  164. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  165. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  166. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  167. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  168. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  169. data/TODO.fixup/30-theme-system-stub.md +46 -0
  170. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  171. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  172. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  173. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  174. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  175. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  176. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  177. data/TODO.fixup/38-note-element-rendering.md +15 -0
  178. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  179. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  180. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  181. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  182. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  183. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  184. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  185. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  186. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  187. data/TODO.fixup/48-background-rect-format.md +15 -0
  188. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  189. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  190. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  191. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  192. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  193. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  194. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  195. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  196. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  197. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  198. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  199. data/TODO.fixup/60-cli-theme-option.md +22 -0
  200. data/TODO.fixup/61-theme-validation.md +28 -0
  201. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  202. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  203. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  204. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  205. data/TODO.fixup/66-styleex-writeback.md +22 -0
  206. data/TODO.fixup/67-delete-dead-code.md +28 -0
  207. data/TODO.fixup/68-figure-parity-status.md +82 -0
  208. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  209. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  210. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  211. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  212. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  213. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  214. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  215. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  216. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  217. data/TODO.fixup/78-note-element-rendering.md +30 -0
  218. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  219. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  220. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  221. data/config/qea_schema.yml +356 -0
  222. data/config/stereotype_colors.yml +15 -0
  223. data/config/themes/119.yml +98 -0
  224. data/config/themes/default.yml +14 -0
  225. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  226. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  227. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  228. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  229. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  230. data/examples/exports/UmlModel_template/model.xml +23 -0
  231. data/examples/exports/basic/UML_EA.DTD +6856 -0
  232. data/examples/exports/basic/model.xml +8039 -0
  233. data/examples/exports/simple/UML_EA.DTD +6856 -0
  234. data/examples/exports/simple/model.xml +585 -0
  235. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  236. data/examples/exports/simple_example/model.xml +23 -0
  237. data/examples/exports/test/UML_EA.DTD +6856 -0
  238. data/examples/exports/test/model.xml +1638 -0
  239. data/examples/qea/basic.qea +0 -0
  240. data/frontend/README.md +51 -0
  241. data/frontend/dist/app.iife.js +24 -0
  242. data/frontend/dist/style.css +1 -0
  243. data/frontend/index.html +26 -0
  244. data/frontend/package-lock.json +2359 -0
  245. data/frontend/package.json +26 -0
  246. data/frontend/src/App.vue +59 -0
  247. data/frontend/src/app.ts +8 -0
  248. data/frontend/src/components/AppHeader.vue +127 -0
  249. data/frontend/src/components/AppSidebar.vue +119 -0
  250. data/frontend/src/components/ClassDetails.vue +247 -0
  251. data/frontend/src/components/DiagramView.vue +79 -0
  252. data/frontend/src/components/PackageDetails.vue +92 -0
  253. data/frontend/src/components/PackageTreeNode.vue +64 -0
  254. data/frontend/src/components/SearchResults.vue +48 -0
  255. data/frontend/src/components/WelcomeView.vue +87 -0
  256. data/frontend/src/shims-vue.d.ts +5 -0
  257. data/frontend/src/stores/dataStore.ts +79 -0
  258. data/frontend/src/stores/uiStore.ts +167 -0
  259. data/frontend/src/styles/main.css +1566 -0
  260. data/frontend/src/types.ts +221 -0
  261. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  262. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  263. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  264. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  265. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  266. data/frontend/tests/e2e/render.spec.ts +188 -0
  267. data/frontend/tsconfig.json +20 -0
  268. data/frontend/vite.config.ts +26 -0
  269. data/frontend/vitest.config.ts +8 -0
  270. data/lib/ea/cli/app.rb +62 -1
  271. data/lib/ea/cli/command/diagrams.rb +37 -28
  272. data/lib/ea/cli/command/diff.rb +46 -0
  273. data/lib/ea/cli/command/export.rb +80 -0
  274. data/lib/ea/cli/command/info.rb +75 -0
  275. data/lib/ea/cli/command/lint.rb +48 -0
  276. data/lib/ea/cli/command/mdg.rb +98 -0
  277. data/lib/ea/cli/command/query.rb +34 -0
  278. data/lib/ea/cli/command/render.rb +135 -0
  279. data/lib/ea/cli/command/svg.rb +43 -6
  280. data/lib/ea/cli/command.rb +7 -0
  281. data/lib/ea/diagram/display_config.rb +184 -0
  282. data/lib/ea/diagram.rb +4 -41
  283. data/lib/ea/diff/change.rb +30 -0
  284. data/lib/ea/diff/comparator.rb +82 -0
  285. data/lib/ea/diff/html_reporter.rb +91 -0
  286. data/lib/ea/diff.rb +15 -0
  287. data/lib/ea/export/json/generator.rb +42 -0
  288. data/lib/ea/export/json.rb +12 -0
  289. data/lib/ea/export/json_schema/generator.rb +77 -0
  290. data/lib/ea/export/json_schema.rb +12 -0
  291. data/lib/ea/export/plantuml/generator.rb +73 -0
  292. data/lib/ea/export/plantuml.rb +12 -0
  293. data/lib/ea/export/xmi/generator.rb +77 -0
  294. data/lib/ea/export/xmi.rb +11 -0
  295. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  296. data/lib/ea/export/xsd/generator.rb +142 -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 +14 -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 +35 -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 +53 -1
  333. data/lib/ea/model/diagram_element.rb +44 -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/query/builder.rb +108 -0
  378. data/lib/ea/query.rb +9 -0
  379. data/lib/ea/render/image_converter.rb +101 -0
  380. data/lib/ea/render.rb +10 -0
  381. data/lib/ea/shapescript/parser.rb +208 -0
  382. data/lib/ea/shapescript/renderer.rb +74 -0
  383. data/lib/ea/shapescript/shape.rb +34 -0
  384. data/lib/ea/shapescript.rb +24 -0
  385. data/lib/ea/sources/qea/adapter.rb +22 -5
  386. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  387. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  388. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  389. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  390. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  391. data/lib/ea/sources/qea/note_builder.rb +58 -0
  392. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  393. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  394. data/lib/ea/sources/qea/package_builder.rb +16 -0
  395. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  396. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  397. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  398. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  399. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  400. data/lib/ea/sources/qea/xref/record.rb +41 -0
  401. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  402. data/lib/ea/sources/qea/xref.rb +27 -0
  403. data/lib/ea/sources/qea.rb +4 -0
  404. data/lib/ea/sources/xmi/adapter.rb +80 -2
  405. data/lib/ea/sources/xmi/annotation_builder.rb +1 -1
  406. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  407. data/lib/ea/sources/xmi/diagram_builder.rb +242 -45
  408. data/lib/ea/sources/xmi/extension_elements.rb +74 -0
  409. data/lib/ea/sources/xmi/extension_geometry_parser.rb +124 -0
  410. data/lib/ea/sources/xmi/extension_style_parser.rb +91 -0
  411. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  412. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  413. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  414. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  415. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  416. data/lib/ea/sources/xmi.rb +7 -0
  417. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  418. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  419. data/lib/ea/spa/output/strategy.rb +34 -0
  420. data/lib/ea/svg/connector_router.rb +134 -0
  421. data/lib/ea/svg/ea_emitter/background.rb +15 -0
  422. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  423. data/lib/ea/svg/ea_emitter/canvas.rb +88 -0
  424. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  425. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  426. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  427. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  428. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  429. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  430. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  431. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  432. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  433. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  434. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  435. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  436. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  437. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  438. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  439. data/lib/ea/svg/ea_emitter/compartment.rb +58 -0
  440. data/lib/ea/svg/ea_emitter/connectors.rb +84 -0
  441. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  442. data/lib/ea/svg/ea_emitter/document.rb +35 -0
  443. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  444. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  445. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  446. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  447. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  448. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  449. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  450. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  451. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  452. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  453. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  454. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  455. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  456. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  457. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  458. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  459. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  460. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  461. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  462. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  463. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  464. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  465. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  466. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  467. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +82 -0
  468. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  469. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  470. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  471. data/lib/ea/svg/ea_emitter/elements.rb +357 -0
  472. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  473. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  474. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  475. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  476. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  477. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  478. data/lib/ea/svg/ea_emitter/labels.rb +130 -0
  479. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  480. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  481. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  482. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  483. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  484. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  485. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  486. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  487. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  488. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  489. data/lib/ea/svg/ea_emitter/markers.rb +404 -0
  490. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  491. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  492. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  493. data/lib/ea/svg/ea_emitter.rb +51 -0
  494. data/lib/ea/svg/parity/checker.rb +105 -0
  495. data/lib/ea/svg/parity/source.rb +59 -0
  496. data/lib/ea/svg/parity/suite.rb +113 -0
  497. data/lib/ea/svg/parity.rb +14 -0
  498. data/lib/ea/svg/stereotype_color_resolver.rb +50 -0
  499. data/lib/ea/svg.rb +4 -15
  500. data/lib/ea/theme/definition.rb +176 -0
  501. data/lib/ea/theme/loader.rb +125 -0
  502. data/lib/ea/theme/registry.rb +96 -0
  503. data/lib/ea/theme.rb +37 -0
  504. data/lib/ea/transformations.rb +5 -88
  505. data/lib/ea/version.rb +4 -1
  506. data/lib/ea.rb +14 -4
  507. data/lib/tasks/svg_bench.rake +87 -0
  508. data/lutaml-uml-0.5.1.gem +0 -0
  509. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  510. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  511. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  512. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  513. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  514. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  515. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  516. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  517. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  518. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  519. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  520. metadata +473 -26
  521. data/lib/ea/diagram/configuration.rb +0 -379
  522. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  523. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  524. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  525. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  526. data/lib/ea/diagram/element_renderers.rb +0 -43
  527. data/lib/ea/diagram/extractor.rb +0 -566
  528. data/lib/ea/diagram/layout_engine.rb +0 -170
  529. data/lib/ea/diagram/path_builder.rb +0 -202
  530. data/lib/ea/diagram/style_parser.rb +0 -42
  531. data/lib/ea/diagram/style_resolver.rb +0 -291
  532. data/lib/ea/diagram/svg_renderer.rb +0 -274
  533. data/lib/ea/diagram/util.rb +0 -73
  534. data/lib/ea/svg/bounds_calculator.rb +0 -74
  535. data/lib/ea/svg/connector_path.rb +0 -138
  536. data/lib/ea/svg/element_box.rb +0 -256
  537. data/lib/ea/svg/renderer.rb +0 -77
  538. data/lib/ea/svg/style_resolver.rb +0 -72
  539. data/lib/ea/transformations/configuration.rb +0 -333
  540. data/lib/ea/transformations/format_registry.rb +0 -366
  541. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  542. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  543. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  544. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -0,0 +1,90 @@
1
+ # TODO-D 17: Remaining Parity Gaps
2
+
3
+ ## Current State (2026-07-26)
4
+
5
+ - Text overlap: **87.2%** (Jaccard, set-equality per `<text>` content)
6
+ - Text count delta: **-83** (-0.4% vs reference; effectively at parity)
7
+ - Shape counts: rect -460 (-20.9%), polygon -166 (-11.2%), path -181 (-5.1%)
8
+ - Within tolerance: 123/188 diagrams (65.4%)
9
+
10
+ ## Closed Gaps
11
+
12
+ 1. QEA `$LLB=...;LLT=...;LRB=...;LRT=...` parsing — only first segment
13
+ carries `$`, the rest are `KEY=` after `;`. Fixed.
14
+ 2. Connector label rendering at LLT/LRT box positions, with role + «property»
15
+ + mult. Fixed.
16
+ 3. Abstract class underscore — the underscore lives in the QEA class name
17
+ itself (`_CityObject`), not added by EA at render time. Fixed.
18
+ 4. Enumeration `«enumeration»` stereotype fallback. Fixed.
19
+ 5. Connector-level stereotype label (e.g. «import» on Package connector).
20
+ Fixed.
21
+ 6. Attribute default value rendering (`name: type [mult] = default`). Fixed.
22
+ 7. Enumeration literal visibility marker (`<text> </text>` empty slot).
23
+ Fixed.
24
+ 8. Multiplicity 1..1 → "1" rendering when paired with role name. Fixed.
25
+
26
+ ## Open Gaps
27
+
28
+ ### Text overlap (87.2% → target ~95%+)
29
+
30
+ 1. **Auto-generated legend block** (~448 missing texts):
31
+ - "凡例" (Legend title, 12pt bold #003060)
32
+ - "GMLで定義されたクラス", "CityGMLで定義されたクラス",
33
+ "i-URで定義されたクラス" (3 color-coded class titles)
34
+ - "GMLに定義されたクラス", "CityGMLに定義されたクラス",
35
+ "i-URに定義されたクラス" (alternative particle に instead of で)
36
+ - EA auto-generates these when a diagram contains color-coded
37
+ stereotype elements. Source data location TBD — likely tied to
38
+ diagram-type or per-diagram MDGDgm flag.
39
+
40
+ 2. **Ghost element discriminator** (TODO-D 20, deferred):
41
+ - Some elements without explicit `font=` in their ObjectStyle get
42
+ ghost rendering (7pt #595959, no attrs); others get full rendering.
43
+ - Tested font_family presence → broke parity (-5000 texts).
44
+ - Tested classifier.package_id match → broke parity (-1500 texts).
45
+ - Real discriminator likely combines: ObjectStyle detail level +
46
+ diagram context + classifier source package. Needs EA.exe
47
+ disassembly reference.
48
+
49
+ 3. **Missing class headers** (~150 instances):
50
+ - Elements like `_AbstractGeometricAggregate`, `_CityObject`,
51
+ `_Site`, `_Feature`, `_TransportationObject` appear in reference
52
+ SVGs but not in t_diagramobjects for the diagram.
53
+ - Cause: either EA renders implicit elements from connector
54
+ endpoints, or the reference SVGs were generated from a different
55
+ QEA version than what we have.
56
+
57
+ 4. **`(from CityGML2.0)` qualifier** (9 instances):
58
+ - EA shows a sub-line for elements imported from a specific
59
+ package version.
60
+ - Source: probably t_object.Package_ID joined with t_package
61
+ version metadata.
62
+
63
+ ### Shape counts
64
+
65
+ 1. **rect -460**:
66
+ - ~3 rects per diagram missing from legend block (container with
67
+ rx=3 + 3 color swatch rects). 188 diagrams × ~3 = ~564 expected,
68
+ close to actual -460 gap.
69
+ - Need legend modeling first (depends on Text gap #1).
70
+
71
+ 2. **polygon -166**:
72
+ - Per-diagram marker counts differ — EA emits duplicate polygons
73
+ at overlapping connector endpoints (e.g. 7 identical triangles
74
+ at one position). Cause unknown.
75
+ - Likely needs investigation of t_diagramlinks.Hidden vs visible
76
+ flag and how EA consolidates markers.
77
+
78
+ 3. **path -181**:
79
+ - EA splits connector paths into individual segments per
80
+ style group. We consolidate same-style paths into one `<g>`.
81
+ - Some diagrams have 30+ path delta — needs path-per-connector
82
+ vs grouped-path mode selection per diagram.
83
+
84
+ ## Next Steps
85
+
86
+ 1. Investigate auto-generated legend feature: how EA detects "this
87
+ diagram needs a legend" and where the legend text/colors come from.
88
+ 2. Find a reliable ghost-element discriminator (likely involves
89
+ per-element display flags we haven't parsed yet).
90
+ 3. Investigate duplicate-polygon emission for overlapping markers.
@@ -0,0 +1,20 @@
1
+ # 18 - Canvas from image_bounds (matching EA)
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ `Canvas.from` now unions BOTH `bounds` and `image_bounds` for each
8
+ element. `image_bounds` captures the rendered extent (logical bounds
9
+ + image padding), so the canvas grows to fit the full visual area.
10
+
11
+ Waterway canvas: 1138×869 → 1163×899 (ref=1138×892)
12
+ Building canvas: 1381×1524 → 1406×1554 (ref=1406×1530, width matches)
13
+
14
+ Remaining diff (25 px width on Waterway) is from image_bounds
15
+ extending further right than EA includes in canvas. Acceptable for
16
+ visual rendering — text and shapes are positioned correctly.
17
+
18
+ ## Files changed
19
+
20
+ - `lib/ea/svg/ea_emitter/canvas.rb` — union bounds + image_bounds
@@ -0,0 +1,42 @@
1
+ # TODO-D 18: Property lookup index (performance + OCP)
2
+
3
+ ## Problem
4
+
5
+ `Ea::Svg::EaEmitter::Labels#property_at_end` walks every Classifier
6
+ and every Property on each Classifier to find a Property by ID:
7
+
8
+ ```ruby
9
+ def property_at_end(association, end_kind)
10
+ id = end_kind == :source ? association.source_id : association.target_id
11
+ return nil unless id && model_index
12
+
13
+ model_index.values.each do |obj|
14
+ next unless obj.is_a?(Ea::Model::Classifier)
15
+ found = (obj.properties || []).find { |p| p.id == id }
16
+ return found if found
17
+ end
18
+ nil
19
+ end
20
+ ```
21
+
22
+ For the 205-diagram plateau QEA this is called once per visible
23
+ association per diagram (~10k calls × ~600 classifiers × ~10
24
+ properties = 60M comparisons). That is the dominant cost in SVG
25
+ rendering.
26
+
27
+ ## Fix
28
+
29
+ Add a lazily-built property index to `Ea::Model::Document` keyed by
30
+ property id. Labels (and any future consumer) calls
31
+ `document.property_by_id[id]` for O(1) lookup.
32
+
33
+ This keeps the lookup logic in the model (model-driven) and avoids
34
+ each consumer rebuilding the same index (DRY).
35
+
36
+ ## Acceptance
37
+
38
+ - `Document#property_index` returns `{ property_id => Property }`.
39
+ - `Labels#property_at_end` uses the index.
40
+ - Parity text overlap remains ≥ 87%.
41
+ - Specs continue to pass.
42
+ - `rake benchmark` (or ad-hoc timing) shows measurable speed-up.
@@ -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