ea 0.4.0 → 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 (529) 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/02-polygon-over-rendering.md +29 -0
  36. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  37. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  38. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  39. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  40. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  41. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  42. data/TODO.diagrams/12-connector-labels.md +40 -0
  43. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  44. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  45. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  46. data/TODO.diagrams/14-center-header-text.md +33 -0
  47. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  48. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  49. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  50. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  51. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  52. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  53. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  54. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  55. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  56. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  57. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  58. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  59. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  60. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  61. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  62. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  63. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  64. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  65. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  66. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  67. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  68. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  69. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  70. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  71. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  72. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  73. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  74. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  75. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  76. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  77. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  78. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  79. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  80. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  81. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  82. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  83. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  84. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  85. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  86. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  87. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  88. data/TODO.diagrams/50-examples-parity.md +85 -0
  89. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  90. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  91. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  92. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  93. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  94. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  95. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  96. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  97. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  98. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  99. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  100. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  101. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  102. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  103. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  104. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  105. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  106. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  107. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  108. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  109. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  110. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  111. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  112. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  113. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  114. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  115. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  116. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  117. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  118. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  119. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  120. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  121. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  122. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  123. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  124. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  125. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  126. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  127. data/TODO.diagrams/README.md +69 -0
  128. data/TODO.diagrams/STATUS.md +73 -0
  129. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  130. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  131. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  132. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  133. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  134. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  135. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  136. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  137. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  138. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  139. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  140. data/TODO.fixup/12-path-style-harmony.md +23 -0
  141. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  142. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  143. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  144. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  145. data/TODO.fixup/17-path-count-calibration.md +27 -0
  146. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  147. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  148. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  149. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  150. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  151. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  152. data/TODO.fixup/24-note-element-rendering.md +23 -0
  153. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  154. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  155. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  156. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  157. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  158. data/TODO.fixup/30-theme-system-stub.md +46 -0
  159. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  160. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  161. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  162. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  163. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  164. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  165. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  166. data/TODO.fixup/38-note-element-rendering.md +15 -0
  167. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  168. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  169. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  170. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  171. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  172. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  173. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  174. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  175. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  176. data/TODO.fixup/48-background-rect-format.md +15 -0
  177. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  178. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  179. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  180. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  181. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  182. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  183. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  184. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  185. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  186. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  187. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  188. data/TODO.fixup/60-cli-theme-option.md +22 -0
  189. data/TODO.fixup/61-theme-validation.md +28 -0
  190. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  191. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  192. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  193. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  194. data/TODO.fixup/66-styleex-writeback.md +22 -0
  195. data/TODO.fixup/67-delete-dead-code.md +28 -0
  196. data/TODO.fixup/68-figure-parity-status.md +82 -0
  197. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  198. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  199. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  200. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  201. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  202. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  203. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  204. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  205. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  206. data/TODO.fixup/78-note-element-rendering.md +30 -0
  207. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  208. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  209. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  210. data/config/qea_schema.yml +356 -0
  211. data/config/themes/119.yml +98 -0
  212. data/config/themes/default.yml +14 -0
  213. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  214. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  215. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  216. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  217. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  218. data/examples/exports/UmlModel_template/model.xml +23 -0
  219. data/examples/exports/basic/UML_EA.DTD +6856 -0
  220. data/examples/exports/basic/model.xml +8039 -0
  221. data/examples/exports/simple/UML_EA.DTD +6856 -0
  222. data/examples/exports/simple/model.xml +585 -0
  223. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  224. data/examples/exports/simple_example/model.xml +23 -0
  225. data/examples/exports/test/UML_EA.DTD +6856 -0
  226. data/examples/exports/test/model.xml +1638 -0
  227. data/examples/qea/basic.qea +0 -0
  228. data/frontend/README.md +51 -0
  229. data/frontend/dist/app.iife.js +24 -0
  230. data/frontend/dist/style.css +1 -0
  231. data/frontend/index.html +26 -0
  232. data/frontend/package-lock.json +2359 -0
  233. data/frontend/package.json +26 -0
  234. data/frontend/src/App.vue +59 -0
  235. data/frontend/src/app.ts +8 -0
  236. data/frontend/src/components/AppHeader.vue +127 -0
  237. data/frontend/src/components/AppSidebar.vue +119 -0
  238. data/frontend/src/components/ClassDetails.vue +247 -0
  239. data/frontend/src/components/DiagramView.vue +79 -0
  240. data/frontend/src/components/PackageDetails.vue +92 -0
  241. data/frontend/src/components/PackageTreeNode.vue +64 -0
  242. data/frontend/src/components/SearchResults.vue +48 -0
  243. data/frontend/src/components/WelcomeView.vue +87 -0
  244. data/frontend/src/shims-vue.d.ts +5 -0
  245. data/frontend/src/stores/dataStore.ts +79 -0
  246. data/frontend/src/stores/uiStore.ts +167 -0
  247. data/frontend/src/styles/main.css +1566 -0
  248. data/frontend/src/types.ts +221 -0
  249. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  250. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  251. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  252. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  253. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  254. data/frontend/tests/e2e/render.spec.ts +188 -0
  255. data/frontend/tsconfig.json +20 -0
  256. data/frontend/vite.config.ts +26 -0
  257. data/frontend/vitest.config.ts +8 -0
  258. data/lib/ea/cli/app.rb +57 -2
  259. data/lib/ea/cli/command/diagrams.rb +37 -28
  260. data/lib/ea/cli/command/diff.rb +46 -0
  261. data/lib/ea/cli/command/export.rb +80 -0
  262. data/lib/ea/cli/command/info.rb +75 -0
  263. data/lib/ea/cli/command/lint.rb +48 -0
  264. data/lib/ea/cli/command/mdg.rb +98 -0
  265. data/lib/ea/cli/command/query.rb +34 -0
  266. data/lib/ea/cli/command/render.rb +135 -0
  267. data/lib/ea/cli/command/svg.rb +8 -15
  268. data/lib/ea/cli/command.rb +7 -0
  269. data/lib/ea/diagram/display_config.rb +184 -0
  270. data/lib/ea/diagram.rb +4 -41
  271. data/lib/ea/diff/change.rb +30 -0
  272. data/lib/ea/diff/comparator.rb +82 -0
  273. data/lib/ea/diff/html_reporter.rb +91 -0
  274. data/lib/ea/diff.rb +15 -0
  275. data/lib/ea/export/json/generator.rb +42 -0
  276. data/lib/ea/export/json.rb +12 -0
  277. data/lib/ea/export/json_schema/generator.rb +77 -0
  278. data/lib/ea/export/json_schema.rb +12 -0
  279. data/lib/ea/export/plantuml/generator.rb +73 -0
  280. data/lib/ea/export/plantuml.rb +12 -0
  281. data/lib/ea/export/xmi/generator.rb +77 -0
  282. data/lib/ea/export/xmi.rb +11 -0
  283. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  284. data/lib/ea/export/xsd/generator.rb +142 -0
  285. data/lib/ea/export/xsd/namespace_registry.rb +82 -0
  286. data/lib/ea/export/xsd.rb +19 -0
  287. data/lib/ea/export.rb +14 -0
  288. data/lib/ea/image/emf_renderer.rb +42 -0
  289. data/lib/ea/image.rb +12 -0
  290. data/lib/ea/lint/engine.rb +30 -0
  291. data/lib/ea/lint/lint_rule.rb +38 -0
  292. data/lib/ea/lint/offense.rb +18 -0
  293. data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
  294. data/lib/ea/lint/rules/duplicate_name.rb +28 -0
  295. data/lib/ea/lint/rules/missing_stereotype.rb +35 -0
  296. data/lib/ea/lint/rules/naming_convention.rb +44 -0
  297. data/lib/ea/lint/rules/orphan_element.rb +28 -0
  298. data/lib/ea/lint/rules.rb +14 -0
  299. data/lib/ea/lint.rb +13 -0
  300. data/lib/ea/mdg/document.rb +120 -0
  301. data/lib/ea/mdg/loader.rb +118 -0
  302. data/lib/ea/mdg/registry.rb +98 -0
  303. data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
  304. data/lib/ea/mdg/xml/applies_to.rb +18 -0
  305. data/lib/ea/mdg/xml/apply.rb +18 -0
  306. data/lib/ea/mdg/xml/content.rb +18 -0
  307. data/lib/ea/mdg/xml/documentation.rb +24 -0
  308. data/lib/ea/mdg/xml/stereotype.rb +28 -0
  309. data/lib/ea/mdg/xml/stereotypes.rb +18 -0
  310. data/lib/ea/mdg/xml/tag.rb +28 -0
  311. data/lib/ea/mdg/xml/tagged_values.rb +18 -0
  312. data/lib/ea/mdg/xml/technology.rb +28 -0
  313. data/lib/ea/mdg/xml/uml_profile.rb +22 -0
  314. data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
  315. data/lib/ea/mdg/xml.rb +24 -0
  316. data/lib/ea/mdg.rb +25 -0
  317. data/lib/ea/model/classifier.rb +4 -0
  318. data/lib/ea/model/constraint.rb +24 -0
  319. data/lib/ea/model/diagram.rb +85 -0
  320. data/lib/ea/model/diagram_connector.rb +34 -0
  321. data/lib/ea/model/diagram_element.rb +16 -0
  322. data/lib/ea/model/document.rb +47 -2
  323. data/lib/ea/model/ghost_label.rb +32 -0
  324. data/lib/ea/model/instance_specification.rb +34 -0
  325. data/lib/ea/model/legend.rb +43 -0
  326. data/lib/ea/model/legend_item.rb +26 -0
  327. data/lib/ea/model/note.rb +28 -0
  328. data/lib/ea/model/operation.rb +2 -0
  329. data/lib/ea/model/property.rb +2 -0
  330. data/lib/ea/model/relationship.rb +2 -0
  331. data/lib/ea/model/signal.rb +14 -0
  332. data/lib/ea/model/slot.rb +19 -0
  333. data/lib/ea/model.rb +8 -0
  334. data/lib/ea/ocl/evaluator.rb +94 -0
  335. data/lib/ea/ocl/nodes.rb +30 -0
  336. data/lib/ea/ocl/parser.rb +133 -0
  337. data/lib/ea/ocl/unsupported_error.rb +7 -0
  338. data/lib/ea/ocl.rb +13 -0
  339. data/lib/ea/qea/factory/document_builder.rb +24 -10
  340. data/lib/ea/qea/factory/generalization_builder.rb +9 -11
  341. data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
  342. data/lib/ea/qea/models/ea_author.rb +35 -0
  343. data/lib/ea/qea/models/ea_glossary.rb +34 -0
  344. data/lib/ea/qea/models/ea_image.rb +53 -0
  345. data/lib/ea/qea/models/ea_implement.rb +32 -0
  346. data/lib/ea/qea/models/ea_list.rb +42 -0
  347. data/lib/ea/qea/models/ea_object_effort.rb +34 -0
  348. data/lib/ea/qea/models/ea_object_problem.rb +37 -0
  349. data/lib/ea/qea/models/ea_object_require.rb +34 -0
  350. data/lib/ea/qea/models/ea_object_resource.rb +34 -0
  351. data/lib/ea/qea/models/ea_object_risk.rb +34 -0
  352. data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
  353. data/lib/ea/qea/models/ea_object_test.rb +35 -0
  354. data/lib/ea/qea/models/ea_object_trx.rb +34 -0
  355. data/lib/ea/qea/models/ea_operation.rb +7 -0
  356. data/lib/ea/qea/models/ea_palette.rb +30 -0
  357. data/lib/ea/qea/models/ea_palette_item.rb +36 -0
  358. data/lib/ea/qea/models/ea_phase.rb +40 -0
  359. data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
  360. data/lib/ea/qea/models/ea_secrypt.rb +34 -0
  361. data/lib/ea/qea/models/ea_version.rb +39 -0
  362. data/lib/ea/qea/models/ea_xref.rb +11 -1
  363. data/lib/ea/qea/models.rb +19 -0
  364. data/lib/ea/qea/services/database_loader.rb +22 -1
  365. data/lib/ea/query/builder.rb +108 -0
  366. data/lib/ea/query.rb +9 -0
  367. data/lib/ea/render/image_converter.rb +101 -0
  368. data/lib/ea/render.rb +10 -0
  369. data/lib/ea/shapescript/parser.rb +208 -0
  370. data/lib/ea/shapescript/renderer.rb +74 -0
  371. data/lib/ea/shapescript/shape.rb +34 -0
  372. data/lib/ea/shapescript.rb +24 -0
  373. data/lib/ea/sources/qea/adapter.rb +22 -5
  374. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  375. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  376. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  377. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  378. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  379. data/lib/ea/sources/qea/note_builder.rb +58 -0
  380. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  381. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  382. data/lib/ea/sources/qea/package_builder.rb +16 -0
  383. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  384. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  385. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  386. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  387. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  388. data/lib/ea/sources/qea/xref/record.rb +41 -0
  389. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  390. data/lib/ea/sources/qea/xref.rb +27 -0
  391. data/lib/ea/sources/qea.rb +4 -0
  392. data/lib/ea/sources/xmi/adapter.rb +80 -2
  393. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  394. data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
  395. data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
  396. data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
  397. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  398. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  399. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  400. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  401. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  402. data/lib/ea/sources/xmi.rb +4 -0
  403. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  404. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  405. data/lib/ea/spa/output/strategy.rb +34 -0
  406. data/lib/ea/svg/connector_router.rb +134 -0
  407. data/lib/ea/svg/ea_emitter/background.rb +2 -3
  408. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  409. data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
  410. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  411. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  412. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  413. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  414. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  415. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  416. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  417. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  418. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  419. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  420. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  421. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  422. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  423. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  424. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  425. data/lib/ea/svg/ea_emitter/compartment.rb +58 -0
  426. data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
  427. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  428. data/lib/ea/svg/ea_emitter/document.rb +15 -24
  429. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  430. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  431. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  432. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  433. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  434. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  435. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  436. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  437. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  438. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  439. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  440. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  441. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  442. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  443. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  444. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  445. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  446. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  447. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  448. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  449. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  450. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  451. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  452. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  453. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +82 -0
  454. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  455. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  456. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  457. data/lib/ea/svg/ea_emitter/elements.rb +283 -95
  458. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  459. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  460. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  461. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  462. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  463. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  464. data/lib/ea/svg/ea_emitter/labels.rb +93 -27
  465. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  466. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  467. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  468. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  469. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  470. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  471. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  472. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  473. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  474. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  475. data/lib/ea/svg/ea_emitter/markers.rb +351 -64
  476. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  477. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  478. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  479. data/lib/ea/svg/ea_emitter.rb +33 -11
  480. data/lib/ea/svg/parity/checker.rb +105 -0
  481. data/lib/ea/svg/parity/source.rb +59 -0
  482. data/lib/ea/svg/parity/suite.rb +113 -0
  483. data/lib/ea/svg/parity.rb +14 -0
  484. data/lib/ea/svg.rb +2 -15
  485. data/lib/ea/theme/definition.rb +176 -0
  486. data/lib/ea/theme/loader.rb +125 -0
  487. data/lib/ea/theme/registry.rb +96 -0
  488. data/lib/ea/theme.rb +37 -0
  489. data/lib/ea/transformations.rb +5 -88
  490. data/lib/ea/version.rb +4 -1
  491. data/lib/ea.rb +14 -4
  492. data/lib/tasks/svg_bench.rake +87 -0
  493. data/lutaml-uml-0.5.1.gem +0 -0
  494. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  495. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  496. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  497. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  498. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  499. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  500. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  501. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  502. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  503. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  504. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  505. metadata +449 -26
  506. data/lib/ea/diagram/configuration.rb +0 -379
  507. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  508. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  509. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  510. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  511. data/lib/ea/diagram/element_renderers.rb +0 -43
  512. data/lib/ea/diagram/extractor.rb +0 -566
  513. data/lib/ea/diagram/layout_engine.rb +0 -170
  514. data/lib/ea/diagram/path_builder.rb +0 -202
  515. data/lib/ea/diagram/style_parser.rb +0 -42
  516. data/lib/ea/diagram/style_resolver.rb +0 -291
  517. data/lib/ea/diagram/svg_renderer.rb +0 -274
  518. data/lib/ea/diagram/util.rb +0 -73
  519. data/lib/ea/svg/bounds_calculator.rb +0 -74
  520. data/lib/ea/svg/connector_path.rb +0 -138
  521. data/lib/ea/svg/element_box.rb +0 -256
  522. data/lib/ea/svg/renderer.rb +0 -77
  523. data/lib/ea/svg/style_resolver.rb +0 -72
  524. data/lib/ea/transformations/configuration.rb +0 -333
  525. data/lib/ea/transformations/format_registry.rb +0 -366
  526. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  527. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  528. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  529. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -0,0 +1,29 @@
1
+ # TODO-D 02 - Polygon over-rendering
2
+
3
+ ## Status: COMPLETE (2026-07-26)
4
+
5
+ ## Root cause
6
+
7
+ Two bugs caused polygon over-rendering:
8
+
9
+ 1. **Markers emitted one polygon per connector, even when multiple
10
+ connectors ended at the same anchor** (e.g., 9 inheritance arrows
11
+ converging on a single base class produced 9 identical triangle
12
+ polygons).
13
+
14
+ 2. **Floating-point waypoint precision caused "identical" markers
15
+ to differ in their point strings** (e.g., `683.37 2041.37` vs
16
+ `683.39 2043.35`), defeating naive `body.uniq` deduplication.
17
+
18
+ ## Fix
19
+
20
+ `Markers#layers` now deduplicates by **rounded anchor point** —
21
+ the first coordinate pair of each polygon/path, rounded to integer.
22
+ Two markers anchored at the same integer pixel are treated as
23
+ duplicates regardless of their other point values.
24
+
25
+ ## Impact (full plateau measurement)
26
+
27
+ | Metric | Before | After |
28
+ |---|---|---|
29
+ | Polygon delta vs ref | +442 (46% over) | -10 (parity) |
@@ -0,0 +1,30 @@
1
+ # TODO-D 03 - Text over-rendering
2
+
3
+ ## Status: COMPLETE (2026-07-26)
4
+
5
+ ## Root cause
6
+
7
+ The tagged values compartment was rendered unconditionally for
8
+ every classifier that had tagged values, regardless of the
9
+ diagram's actual display configuration. The reference SVGs only
10
+ show tagged values when the per-element style has `Tag=1`.
11
+
12
+ The previous fix (TODO 70) added the TaggedValueRenderer but did
13
+ not gate it on the element-level Tag flag.
14
+
15
+ ## Fix
16
+
17
+ `Elements#groups_for` now reads `element.show_tagged_values`
18
+ (parsed from the per-element style `Tag=0/1` field) and only
19
+ renders the tagged value compartment when the flag is set.
20
+
21
+ The `DiagramElement` model gained `show_tagged_values` boolean.
22
+ `ExtensionStyleParser` populates it from the `Tag=` style key.
23
+ `DiagramBuilder` passes it through to the model.
24
+
25
+ ## Impact (full plateau measurement)
26
+
27
+ | Metric | Before | After |
28
+ |---|---|---|
29
+ | Text delta vs ref | +5134 (29% over) | -252 (parity) |
30
+ | Text overlap avg | 57% | 62% |
@@ -0,0 +1,33 @@
1
+ # TODO-D 04 - Rect under-rendering
2
+
3
+ ## Status: WONTFIX (data drift) (2026-07-26)
4
+
5
+ ## Investigation
6
+
7
+ Across 185 plateau diagrams, we under-render 221 rects (11%). The
8
+ worst case (bldg_1) shows 18 rects in reference vs 11 in ours —
9
+ 7 missing rects.
10
+
11
+ Examining bldg_1's reference SVG reveals classifiers placed on the
12
+ diagram (e.g., `uro::ReservoirFloodingRiskAttribute`) that are NOT
13
+ in the XMI's `<diagram>/<elements>` block. The classifier exists
14
+ in the uml:Model hierarchy, but no `<element subject="..."/>` ref
15
+ places it on this diagram.
16
+
17
+ ## Conclusion
18
+
19
+ The reference SVGs were generated from a MORE COMPLETE XMI than
20
+ the one we have. The XMI/SVG pair has drift — the SVG has
21
+ placements the XMI doesn't carry.
22
+
23
+ This is a fixture data issue, not an emitter bug. We cannot render
24
+ elements the XMI doesn't place. Closing this gap requires either:
25
+
26
+ 1. Regenerating the plateau XMI from the same QEA source that
27
+ produced the reference SVGs.
28
+ 2. Implementing a "implicit placement" mode that auto-places
29
+ classifiers referenced by connectors but not in `<elements>`.
30
+ This would be guesswork — we don't know where EA placed them.
31
+
32
+ Option 1 is the right path. Marking this wontfix pending fixture
33
+ regeneration.
@@ -0,0 +1,51 @@
1
+ # TODO-D 05 - Canvas dimension parity
2
+
3
+ ## Status: PARTIAL (2026-07-26)
4
+
5
+ ## Current state
6
+
7
+ | | Avg delta | Range |
8
+ |---|---|---|
9
+ | Width | +87px | -403 to +1482 |
10
+ | Height | +576px | -109 to +4833 |
11
+
12
+ For diagrams where XMI matches reference content (small delta),
13
+ the canvas is consistently ~20-25 px wider and ~7-15 px taller
14
+ than reference. That delta is the frame margin.
15
+
16
+ ## Frame margin rule
17
+
18
+ Reference SVGs use non-uniform frame margins around element
19
+ bounds:
20
+
21
+ - Left: 35 px (body left to canvas left)
22
+ - Right: 50 px (body right to canvas right)
23
+ - Top: 60 px (body top to canvas top)
24
+ - Bottom: 36 px (body bottom to canvas bottom)
25
+
26
+ These come from EA's frame outer border (6 px from canvas edge)
27
+ plus an additional 29/44/54/30 px inset for the body. The asymmetry
28
+ exists because:
29
+
30
+ - Top: extra space for the frame tab label (extends from y=6 to
31
+ y=26 plus padding to body_top=60).
32
+ - Right/bottom: extra space for shadow / image padding.
33
+
34
+ ## Fix attempted
35
+
36
+ `BoundsCalculator#compute` already reserves 20 px above each
37
+ Package element for the tab polygon. Adding frame-aware padding
38
+ to all four sides would close the per-diagram gap on small
39
+ diagrams.
40
+
41
+ ## Why deferred
42
+
43
+ The 576 px average height delta dwarfs the frame margin issue.
44
+ Most of the height over-rendering is from XMI/SVG drift (we render
45
+ elements the reference SVG doesn't show). Until that's resolved
46
+ (fixture regeneration — see TODO-D 04), fixing the frame margin
47
+ won't move the aggregate metric meaningfully.
48
+
49
+ The small-diagram match (those closest to reference) shows the
50
+ frame margin fix would close ~5-10 px of the remaining 20-25 px
51
+ delta. Not enough to justify the added complexity right now.
@@ -0,0 +1,39 @@
1
+ # TODO-D 06 - Group layering parity
2
+
3
+ ## Status: PARTIAL (2026-07-26)
4
+
5
+ ## Current state
6
+
7
+ EA emits each `<rect>`, `<polygon>`, `<path>`, `<text>` inside its
8
+ own `<g>` block with a specific `style` attribute. Style strings
9
+ differ by:
10
+
11
+ - stroke_width (1 vs 2)
12
+ - stroke_linecap (round vs square)
13
+ - stroke_linejoin (bevel vs miter)
14
+ - fill color
15
+ - stroke color
16
+ - opacity
17
+
18
+ Our emitter groups output by `style_key` (a Symbol like
19
+ `:diamond_filled` or `:triangle_open`), with the style string
20
+ looked up from a small dispatch table. This matches EA's grouping
21
+ semantics for most cases.
22
+
23
+ ## Note element body
24
+
25
+ The Note element body was previously emitted as a `<polygon>`. EA
26
+ emits it as a closed `<path d="M...L...Z"/>` with
27
+ `stroke-linejoin=bevel` and `stroke-linecap=square`. Fixed in
28
+ commit f4fa55f.
29
+
30
+ ## Remaining gap
31
+
32
+ EA sometimes uses different `stroke-linecap` for the same shape
33
+ type depending on whether it's a "decorative" element (square cap)
34
+ or a "connector" element (round cap). Our style dispatch table
35
+ doesn't distinguish these. Visible in:
36
+
37
+ - Note bodies (square) vs classifier rects (round) — fixed
38
+ - Connector lines (round) vs divider lines (round) — both round
39
+ - Visibility icon rects inside attribute compartments — TBD
@@ -0,0 +1,65 @@
1
+ # TODO-D 07 - Full text content fidelity
2
+
3
+ ## Status: PARTIAL (62% overlap) (2026-07-26)
4
+
5
+ ## Current state
6
+
7
+ Text overlap (Jaccard index of text content sets between our SVG
8
+ and reference SVG):
9
+
10
+ - Average across 185 plateau diagrams: **62%**
11
+ - Best diagrams: ~95%
12
+ - Worst diagrams: ~20%
13
+
14
+ ## What's working
15
+
16
+ - Classifier names and stereotypes render correctly when the
17
+ element is placed on the diagram.
18
+ - Attribute text matches when the element has Tag=0 (no tagged
19
+ values compartment).
20
+ - Visibility prefixes ("+ ", "- ") match.
21
+ - Multiplicity brackets match.
22
+
23
+ ## What's still off
24
+
25
+ ### 1. Tagged value visibility (mostly fixed, edge cases remain)
26
+ Reference SVGs don't render the "tags" header compartment except
27
+ on 2 diagrams. Our Tag-flag gating brought text overlap from 41%
28
+ to 62%. Remaining misses are likely from elements where the
29
+ `Tag=1` flag is set but the reference still doesn't render tags
30
+ — possibly tied to stereotype source (profile-applied vs
31
+ user-defined tagged values).
32
+
33
+ ### 2. Qualified name vs simple name
34
+ Reference uses qualified names like
35
+ `uro::TrafficVolumeAttribute`. Our output sometimes uses the
36
+ short name only. The qualified_name attribute is set on the
37
+ Classifier model, but the renderer doesn't always pick it up
38
+ correctly when the element is placed on a sub-package diagram.
39
+
40
+ ### 3. Attribute ordering
41
+ Within a classifier's attribute compartment, EA sorts attributes
42
+ by some criteria (possibly visibility then alphabetical). Our
43
+ ordering follows the XMI's `ownedAttribute` order, which may
44
+ differ.
45
+
46
+ ### 4. Attribute visibility color
47
+ Reference uses different fill colors for public/protected/private
48
+ attributes (`#66413F` for some, `#595959` for others). Our
49
+ renderer uses a single text color from the theme.
50
+
51
+ ## Path to 100% text overlap
52
+
53
+ 1. Characterize the tagged value visibility rule by examining
54
+ which elements with `Tag=1` still don't render tags in
55
+ reference (estimated 5-10 diagrams).
56
+
57
+ 2. Always use `classifier.qualified_name` for the header label
58
+ (currently mixed).
59
+
60
+ 3. Sort attributes by visibility prefix, then alphabetical.
61
+
62
+ 4. Apply theme.color_for_attribute_visibility for attribute text.
63
+
64
+ Each of these is a separate fix. None require architectural
65
+ changes.
@@ -0,0 +1,46 @@
1
+ # TODO-D 08-11 - QEA-direct pipeline
2
+
3
+ ## Status: COMPLETE (2026-07-27)
4
+
5
+ ## What was wired
6
+
7
+ 1. **TODO-D 08**: QEA DiagramBuilder propagates StyleEx to
8
+ Diagram.style_ex. Theme=:119 is now correctly detected from QEA.
9
+
10
+ 2. **TODO-D 09**: IdNormalizer.to_eaid converts `{GUID}` to
11
+ `EAID_<guid-with-underscores>` for matching reference SVG
12
+ filenames. The QEA stores diagram GUIDs in braces-dashes form;
13
+ the reference SVGs use the EAID_ underscore form.
14
+
15
+ 3. **TODO-D 10**: New QEA NoteBuilder parses t_object rows with
16
+ object_type="Note" or "Text". These render as Package-shaped
17
+ diagram boxes (not dog-eared Notes — QEA's "Note" type is a
18
+ diagrammatic Package-shape box for free text, distinct from
19
+ XMI's uml:Note).
20
+
21
+ Also: connector_type from t_connector.Type is now propagated
22
+ to DiagramConnector so Marker::Diamond matches Aggregation
23
+ and Composition (was being dropped, all connectors fell back
24
+ to Association).
25
+
26
+ 4. **TODO-D 11**: spec/ea/svg/qea_regression_spec.rb renders all
27
+ 188 diagrams directly from the QEA against reference SVGs.
28
+ Passes at the 60 percent within-tolerance threshold.
29
+
30
+ ## Parity delta from QEA-direct fixes
31
+
32
+ | Metric | Before | After |
33
+ |---|---|---|
34
+ | Polygon delta | n/a (was reading XMI) | -166 (was -842 at start) |
35
+ | Path delta | n/a | -181 |
36
+ | Text overlap avg | 10% (initial QEA-direct) | 56% |
37
+ | Within tolerance | 60.6% | 65.4% |
38
+
39
+ ## Bonus fixes shipped alongside
40
+
41
+ - ClassifierBuilder no longer prepends package name to
42
+ qualified_name (QEA t_object.Name already includes the prefix).
43
+ - HeaderLines no longer renders fallback stereotype labels.
44
+ - AttributeRenderer no longer double-colons type names.
45
+ - bounds_from_rect uses min/max of rect values (QEA stores rect
46
+ with recttop > rectbottom).
@@ -0,0 +1,40 @@
1
+ # TODO-D 12 - Connector role/multiplicity labels
2
+
3
+ ## Status: COMPLETE (2026-07-27)
4
+
5
+ ## What changed
6
+
7
+ Labels renderer refactored to support both XMI and QEA paths:
8
+
9
+ 1. **Property lookup (XMI path)**: When the Association's
10
+ source_id/target_id reference Property IDs, look them up and
11
+ render the role name + multiplicity from the Property fields.
12
+
13
+ 2. **Association fields (QEA path)**: Fall back to the
14
+ Association's own source_role_name, target_role_name,
15
+ source_multiplicity_lower/upper, target_multiplicity_lower/upper
16
+ fields. These are populated by QEA's t_connector.sourcerole/
17
+ destrole/sourcecard/destcard columns.
18
+
19
+ 3. **Default label offset**: When label_boxes aren't populated
20
+ (QEA path — the geometry parser doesn't yet extract them),
21
+ use a default offset (anchor + 5, -10) so labels render near
22
+ the connector endpoints rather than being silently dropped.
23
+
24
+ ## Acceptance
25
+
26
+ QEA-direct parity measurement:
27
+
28
+ | Metric | Before | After |
29
+ |---|---|---|
30
+ | Text delta vs ref | -2728 (14% under) | -883 (5% under) |
31
+ | Text overlap avg | 56.0% | 62.8% |
32
+
33
+ ## Remaining
34
+
35
+ - QEA t_diagramlinks.Geometry parser doesn't extract LLB/LRB
36
+ boxes. Adding that would let labels render at EA's recorded
37
+ pixel positions rather than the default offset.
38
+ - The XMI-encoded `<text>` ordering within Labels is still
39
+ suboptimal — EA emits role name and multiplicity in a single
40
+ `<g>` block, we emit each as a separate text element.
@@ -0,0 +1,24 @@
1
+ # 12 - Per-Entity SVG Layer Ordering
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ Restructured `Ea::Svg::EaEmitter::Document` to emit a flat sequence
8
+ of top-level `<g>` elements matching EA's per-entity layering:
9
+
10
+ - `Elements#groups` returns Array of per-element `<g>` blocks
11
+ (shape → header → divider → attrs)
12
+ - `Connectors#groups` returns Array of one `<g>` per connector line
13
+ - `Markers#groups` returns Array of one `<g>` per connector marker
14
+ - `Document#connector_layers` interleaves line + marker per connector
15
+
16
+ Verified on Waterway: 50 top-level `<g>` (ours) vs 47 (ref) — the
17
+ 3-group delta is due to the spurious frame element (TODO 14).
18
+
19
+ ## Files changed
20
+
21
+ - `lib/ea/svg/ea_emitter/document.rb` — interleaves line/marker per connector
22
+ - `lib/ea/svg/ea_emitter/elements.rb` — `groups` method, per-element arrays
23
+ - `lib/ea/svg/ea_emitter/connectors.rb` — `groups` method, one `<g>` per connector
24
+ - `lib/ea/svg/ea_emitter/markers.rb` — `groups` method, one `<g>` per marker
@@ -0,0 +1,20 @@
1
+ # 13 - Marker Encoding: Polygon vs Path by Shape
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ Refactored `Ea::Svg::EaEmitter::Markers` to dispatch marker shape
8
+ via a `Spec` value object. Each marker emits the correct element type:
9
+
10
+ - Diamond (Aggregation/Composition) → `<polygon>` 4 points
11
+ - Open triangle (Generalization/Realization/Dependency) → `<polygon>` 3 points, white fill
12
+ - Filled arrow (Association navigability) → `<path d="M .. L .. L .."/>` 3 points
13
+
14
+ Marker placement is direction-aware: "Destination -> Source" flips
15
+ which end holds the marker.
16
+
17
+ ## Files changed
18
+
19
+ - `lib/ea/svg/ea_emitter/markers.rb` — Spec struct + dispatch
20
+ - `spec/ea/svg/ea_emitter/markers_spec.rb` — updated to reflect arrow emission
@@ -0,0 +1,33 @@
1
+ # TODO-D 13 - Per-text-type font sizes
2
+
3
+ ## Status: COMPLETE (2026-07-27)
4
+
5
+ ## Context
6
+
7
+ Reference SVGs use different font sizes for different text types:
8
+ - 9pt for classifier compartments (header, attributes, operations,
9
+ enumeration literals, tagged values)
10
+ - 7pt for diagram-level text (frame label, swimlane names)
11
+ - 12pt for some classifier names (rare)
12
+
13
+ Theme :119 specifies font_size=7pt — that's the DIAGRAM-LEVEL font
14
+ size, not the element-level default. Element compartment text uses
15
+ 9pt regardless of theme.
16
+
17
+ ## What changed
18
+
19
+ - `FontResolver::DEFAULT_ELEMENT_FONT_SIZE = 9` — replaces the
20
+ previous default of 13 (which was the Yu Gothic UI locale default)
21
+ - `FontResolver#size_for` no longer falls back to `theme.font_size`
22
+ for elements — only diagram-level text uses theme font size
23
+ - `HeaderRenderer` and `AttributeRenderer` gain `size_unit:`
24
+ parameter (defaults to `"pt"`) and propagate it to TextRenderer
25
+ - `Elements#groups_for` reads `size_unit` from FontResolver and
26
+ passes through to all renderers
27
+
28
+ ## Acceptance
29
+
30
+ - Reference text uses `font-size:9pt` for classifier compartments
31
+ - Our text now emits `font-size:9pt` (was `font-size:7px`)
32
+ - Text overlap metric unchanged (counts content, not formatting)
33
+ - 1612 specs pass
@@ -0,0 +1,33 @@
1
+ # TODO-D 14 - Center classifier header text
2
+
3
+ ## Status: COMPLETE (2026-07-27)
4
+
5
+ ## Context
6
+
7
+ EA centers stereotype and classifier-name text horizontally within
8
+ the element bounds. SVG `<text x=>` is the LEFT edge of the text,
9
+ not the center, so we need to compute:
10
+
11
+ left_edge = bounds.center_x - text_width / 2
12
+
13
+ Our previous code emitted `x = bounds.center_x` directly, which
14
+ placed the LEFT EDGE at the center — making all text appear
15
+ right-of-center.
16
+
17
+ ## What changed
18
+
19
+ `HeaderRenderer.center_x_for(text, bounds, size)` computes the
20
+ left edge from a text-width approximation:
21
+
22
+ text_width = text.length * size * 0.55
23
+
24
+ The factor 0.55 approximates average Carlito character width as a
25
+ fraction of point size. Not pixel-perfect (EA uses GDI text
26
+ metrics) but visually centered.
27
+
28
+ ## Acceptance
29
+
30
+ - Stereotype label now visually centered in element box
31
+ - Classifier name now visually centered
32
+ - Close to reference x positions (within ~5px due to text-width
33
+ approximation)
@@ -0,0 +1,18 @@
1
+ # 14 - Filter Diagram-Frame / Note Elements
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ Added `Elements#frame_element?` predicate that skips elements
8
+ matching the diagram-frame pattern:
9
+ - `background_color == -1` (sentinel for "no fill / default"), AND
10
+ - Classifier name is nil/empty, AND
11
+ - Classifier has no properties
12
+
13
+ Verified: Waterway top-level `<g>` count drops 50→47, rect count
14
+ drops 10→9 — both now match EA reference exactly.
15
+
16
+ ## Files changed
17
+
18
+ - `lib/ea/svg/ea_emitter/elements.rb` — `frame_element?` predicate
@@ -0,0 +1,47 @@
1
+ # TODO-D 15 - Deep dead code audit
2
+
3
+ ## Status: COMPLETE (2026-07-27)
4
+
5
+ ## Audit findings
6
+
7
+ All production subsystems in `lib/ea/` are actively used:
8
+
9
+ | Subsystem | Used by | Status |
10
+ |---|---|---|
11
+ | `Ea::Model` | All adapters and renderers | KEEP |
12
+ | `Ea::Sources::Qea` | `Ea.parse`, `Ea::Sources::Qea::Adapter.from_path` | KEEP |
13
+ | `Ea::Sources::Xmi` | `Ea::Sources::Xmi::Adapter.from_path` | KEEP |
14
+ | `Ea::Svg::EaEmitter` | `ea svg` CLI command | KEEP |
15
+ | `Ea::Theme` | Diagram.theme resolution | KEEP |
16
+ | `Ea::Diagram::DisplayConfig` | `Diagram#display_config` | KEEP |
17
+ | `Ea::Qea` (SQLite layer) | `Ea::Sources::Qea::*` adapters | KEEP |
18
+ | `Ea::Xmi` | `Ea::Sources::Xmi::*` adapters | KEEP |
19
+ | `Ea::Spa` | `ea spa` CLI command | KEEP |
20
+ | `Ea::Transformers` | `ea convert` CLI command | KEEP |
21
+ | `Ea::Bridge` | `Ea::Transformations.to_uml` | KEEP |
22
+ | `Ea::Transformations` | `RepositoryBuilder` for bridge CLI | KEEP |
23
+
24
+ ## Code quality audit
25
+
26
+ | Rule | Status |
27
+ |---|---|
28
+ | No `send` to call private methods | CLEAN |
29
+ | No `instance_variable_set`/`get` | CLEAN (only mentioned in a comment) |
30
+ | No `respond_to?` for type checks | CLEAN (replaced with `is_a?` earlier) |
31
+ | No `require_relative` for library code | CLEAN (one in `lib/ea.rb` for VERSION — standard gemspec pattern) |
32
+ | autoload for all internal loading | CLEAN |
33
+
34
+ ## Architecture observations
35
+
36
+ The codebase follows OCP well:
37
+ - `Marker::Kind` subclasses registered via `Marker::Registry.register`
38
+ - `Theme::Definition` immutable value object with `#with` for variants
39
+ - `Theme::Registry` auto-loads from `config/themes/*.yml`
40
+ - `Element::*Renderer` classes are independent — adding a new
41
+ compartment type doesn't modify existing renderers
42
+
43
+ ## DRY
44
+
45
+ Found one minor duplication: `Labels` and `AttributeRenderer` both
46
+ have text-emission logic. Consolidated into shared `TextRenderer`
47
+ calls. No further DRY violations observed.
@@ -0,0 +1,24 @@
1
+ # 15 - Per-Element Font from Style
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ Added `Ea::Svg::EaEmitter::FontResolver` that resolves font family +
8
+ size + weight + style per element, using EA's fallback chain:
9
+
10
+ 1. Element-level `font_family` / `font_size` (set from EA's
11
+ `font=NAME;fontsz=PCT` style fields via ExtensionStyleParser)
12
+ 2. Diagram-level default (most common non-nil value across elements)
13
+ 3. Module default (`Yu Gothic UI`, `13`)
14
+
15
+ `Elements` emitter delegates font resolution to FontResolver so the
16
+ fallback rule lives in one place (MECE).
17
+
18
+ ## Files changed
19
+
20
+ - `lib/ea/svg/ea_emitter/font_resolver.rb` — new resolver
21
+ - `lib/ea/svg/ea_emitter.rb` — autoload registration
22
+ - `lib/ea/svg/ea_emitter/elements.rb` — uses FontResolver
23
+ - `spec/ea/svg/ea_emitter/font_resolver_spec.rb` — 9 specs covering
24
+ explicit, fallback, and empty-diagram cases
@@ -0,0 +1,30 @@
1
+ # 16 - Canvas Bounds Include Marker Extent
2
+
3
+ ## Status: DONE (2026-07-23)
4
+
5
+ ## Outcome
6
+
7
+ `Canvas.from` now extends the points list with `MARKER_EXTENT=15`
8
+ padding around each connector's source and target waypoints. This
9
+ prevents markers (diamonds, triangles, arrows) from being clipped
10
+ when they project beyond element bounds.
11
+
12
+ For Waterway this didn't change the canvas size (markers fit within
13
+ existing element bounds), but for diagrams where arrows extend below
14
+ or beside elements, the canvas now grows accordingly.
15
+
16
+ ## Remaining diff
17
+
18
+ Waterway height: ours 869 vs ref 892 (23 px short). Root cause:
19
+ EA's renderer uses `image_bounds` (with 12 px image padding) for
20
+ certain elements while we use logical `bounds`. The 23 px diff
21
+ matches the difference between logical and image_bounds for the
22
+ lowest element (FacilityIdAttribute: logical bottom=895, image
23
+ bottom=918). To close this fully we'd need to compute canvas from
24
+ image_bounds union (matching EA's behavior), but this would also
25
+ require translating elements by image_bounds rather than logical
26
+ bounds — a larger change deferred to TODO 18.
27
+
28
+ ## Files changed
29
+
30
+ - `lib/ea/svg/ea_emitter/canvas.rb` — `marker_extents` + concat into points
@@ -0,0 +1,91 @@
1
+ # TODO-D 16 - Document all remaining parity gaps
2
+
3
+ ## Status: LIVING DOCUMENT (2026-07-27)
4
+
5
+ ## Current parity (QEA-direct, 188 diagrams)
6
+
7
+ | Metric | Value |
8
+ |---|---|
9
+ | Within shape-count tolerance | 123/188 (65.4%) |
10
+ | Text overlap avg | 62.8% |
11
+ | Polygon delta | -166 |
12
+ | Path delta | -181 |
13
+ | Text delta | -883 |
14
+ | Rect delta | -460 |
15
+
16
+ ## Byte-level diff status (maintenance diagram)
17
+
18
+ The simplest diagram (`maintenance`, 1 classifier) now matches
19
+ reference byte-for-byte on:
20
+
21
+ - Canvas dimensions (254x177) ✓
22
+ - Element position (35, 40) ✓
23
+ - Background rect dimensions ✓
24
+ - Frame border path coordinates ✓
25
+
26
+ Remaining byte-level differences on maintenance:
27
+
28
+ ### 1. Build ID
29
+ - Ours: `Build: 1628`
30
+ - Ref: `Build: 1624`
31
+
32
+ The QEA was authored with build 1628 (newer). The reference SVGs
33
+ were exported with build 1624. Either change the BUILD_ID constant
34
+ or accept the difference.
35
+
36
+ ### 2. Frame tab width
37
+ - Ours: `points="6 26 135 26 148 13 148 6 6 6 6 26"` (width=129)
38
+ - Ref: `points="6 26 86 26 99 12 99 6 6 6 6 26"` (width=80)
39
+
40
+ EA calculates the tab width from GDI text metrics on the label
41
+ "class maintenance". Our calculation uses `label.length * 7 + 10`
42
+ which overshoots. Need a better text-width approximation, OR
43
+ match EA's hardcoded minimum (80px).
44
+
45
+ ### 3. textLength attribute
46
+ - Ours: `textLength="65"`
47
+ - Ref: `textLength="73"`
48
+
49
+ Same root cause as #2 — text-width approximation differs from
50
+ GDI metrics.
51
+
52
+ ### 4. Whitespace formatting
53
+ - Ours: extra blank lines between `<g>` blocks
54
+ - Ref: no blank lines, tabs for indentation
55
+
56
+ Cosmetic only — doesn't affect rendering.
57
+
58
+ ## Aggregate parity gaps (across 188 diagrams)
59
+
60
+ ### Polygon under-rendering (-166)
61
+ Source-side diamond markers on Aggregation connectors. The diamond
62
+ anchor positions drift from EA because connector SX/SY/EX/EY
63
+ interpretation isn't exact. Markers render but at slightly wrong
64
+ positions, breaking the per-anchor dedup match.
65
+
66
+ ### Path under-rendering (-181)
67
+ EA combines connector line + arrow head into one `<path>` with
68
+ multiple `M` sub-paths. We emit them as separate paths. Restructure
69
+ `Connectors` renderer to emit combined paths per connector.
70
+
71
+ ### Text under-rendering (-883)
72
+ Three categories:
73
+ 1. Connector label positions (we use default offset; EA records
74
+ exact LLB/LRB boxes in t_diagramlinks.Geometry)
75
+ 2. Attribute defaults (e.g. `lod: integerBetween0and4 = 1` — we
76
+ drop the `= default_value` part)
77
+ 3. Stereotype-tagged-value text (we render the «property»
78
+ stereotype label on some diagrams where ref doesn't)
79
+
80
+ ### Rect under-rendering (-460)
81
+ Mostly classifier-box compartment rects that EA emits as inner
82
+ rects (not paths) for some diagram configurations. Also legend
83
+ swatches inside Note elements. Not yet investigated thoroughly.
84
+
85
+ ## Priority order for closing remaining gaps
86
+
87
+ 1. **Combined connector paths** — fixes path -181, no architectural change
88
+ 2. **QEA geometry parser for LLB/LRB** — fixes most of text -883
89
+ 3. **Attribute default value rendering** — fixes some text delta
90
+ 4. **Frame tab width calculation** — fixes byte-diff on simple diagrams
91
+ 5. **Rect inner-compartment rendering** — fixes rect -460