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
@@ -1,274 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ea
4
- module Diagram
5
- # Main SVG renderer for EA diagrams
6
- class SvgRenderer
7
- include Ea::Diagram::Util
8
-
9
- attr_reader :diagram_renderer, :options, :bounds, :style_resolver
10
-
11
- DEFAULT_OPTIONS = {
12
- padding: 20,
13
- background_color: "#ffffff",
14
- grid_visible: false,
15
- interactive: false,
16
- css_classes: [],
17
- }.freeze
18
-
19
- def initialize(diagram_renderer, options = {})
20
- @diagram_renderer = diagram_renderer
21
- @options = DEFAULT_OPTIONS.merge(options)
22
- @bounds = diagram_renderer.bounds
23
- @style_resolver = StyleResolver.new(options[:config_path])
24
- end
25
-
26
- # Render the complete SVG diagram
27
- # @return [String] Complete SVG content
28
- def render # rubocop:disable Metrics/AbcSize
29
- svg_content = +""
30
- svg_content << svg_header
31
- svg_content << defs_section
32
- svg_content << background_layer
33
- svg_content << grid_layer if options[:grid_visible]
34
- svg_content << connectors_layer
35
- svg_content << elements_layer
36
- svg_content << interactive_layer if options[:interactive]
37
- svg_content << svg_footer
38
- svg_content
39
- end
40
-
41
- def determine_marker_type(connector_type) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
42
- normalized_type = connector_type.to_s.downcase
43
-
44
- case normalized_type
45
- when "generalization", "inheritance"
46
- { end: "url(#generalization-arrow)" }
47
- when "aggregation"
48
- { start: "url(#aggregation-arrow)" }
49
- when "composition"
50
- { start: "url(#composition-arrow)" }
51
- when "dependency"
52
- { end: "url(#dependency-arrow)" }
53
- when "realization", "implementation"
54
- { end: "url(#realization-arrow)" }
55
- else
56
- { end: "url(#association-arrow)" }
57
- end
58
- end
59
-
60
- private
61
-
62
- def svg_header # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
63
- # Bounds already include padding from LayoutEngine
64
- width = bounds[:width]
65
- height = bounds[:height]
66
-
67
- # Normalize viewBox to start at 0,0 (matching EA export format)
68
- # Shift all content to positive coordinates
69
- offset_x = bounds[:x].negative? ? bounds[:x].abs : 0
70
- offset_y = bounds[:y].negative? ? bounds[:y].abs : 0
71
- total_width = width + offset_x
72
- total_height = height + offset_y
73
-
74
- view_box = "0 0 #{total_width} #{total_height}"
75
-
76
- # Format width/height in cm (matching EA export format)
77
- width_cm = format("%.2f", (total_width / 37.7952755906).round(2))
78
- height_cm = format("%.2f", (total_height / 37.7952755906).round(2))
79
-
80
- <<~SVG
81
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
82
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
83
-
84
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="#{width_cm}cm" height="#{height_cm}cm" viewBox="#{view_box}">
85
- <title></title>
86
- <desc>Created with Enterprise Architect (Build: 1624) 2</desc>
87
- SVG
88
- end
89
-
90
- def defs_section
91
- <<~SVG
92
- <defs>
93
- <style type="text/css">
94
- <![CDATA[
95
- .lutaml-diagram-element { cursor: pointer; }
96
- .lutaml-diagram-element:hover { opacity: 0.8; }
97
- .lutaml-diagram-connector { fill: none; stroke: #000000; stroke-width: 1; }
98
- .lutaml-diagram-connector:hover { stroke-width: 2; }
99
- .lutaml-diagram-grid { stroke: #e0e0e0; stroke-width: 0.5; }
100
- .lutaml-diagram-text { font-family: Arial, sans-serif; font-size: 11px; }
101
- .lutaml-diagram-stereotype { font-style: italic; font-size: 9px; }
102
- .lutaml-diagram-class-name { font-weight: bold; font-size: 12px; }
103
- ]]>
104
- </style>
105
- <!-- EA-style arrow markers -->
106
- <marker id="generalization-arrow" markerWidth="10" markerHeight="7"
107
- refX="9" refY="3.5" orient="auto">
108
- <polygon points="0 0, 10 3.5, 0 7" fill="#FFFFFF" stroke="#000000" stroke-width="1" />
109
- </marker>
110
- <marker id="association-arrow" markerWidth="10" markerHeight="7"
111
- refX="9" refY="3.5" orient="auto">
112
- <polygon points="0 0, 10 3.5, 0 7" fill="#000000" />
113
- </marker>
114
- <marker id="aggregation-arrow" markerWidth="12" markerHeight="12"
115
- refX="6" refY="6" orient="auto">
116
- <polygon points="6,0 12,6 6,12 0,6" fill="#FFFFFF" stroke="#000000" stroke-width="1" />
117
- </marker>
118
- <marker id="composition-arrow" markerWidth="12" markerHeight="12"
119
- refX="6" refY="6" orient="auto">
120
- <polygon points="6,0 12,6 6,12 0,6" fill="#000000" stroke="#000000" stroke-width="1" />
121
- </marker>
122
- <marker id="dependency-arrow" markerWidth="10" markerHeight="7"
123
- refX="9" refY="3.5" orient="auto">
124
- <polygon points="0 0, 10 3.5, 0 7" fill="#000000" />
125
- </marker>
126
- <marker id="realization-arrow" markerWidth="10" markerHeight="7"
127
- refX="9" refY="3.5" orient="auto">
128
- <polygon points="0 0, 10 3.5, 0 7" fill="#FFFFFF" stroke="#000000" stroke-width="1" />
129
- </marker>
130
- </defs>
131
- SVG
132
- end
133
-
134
- def background_layer # rubocop:disable Metrics/AbcSize
135
- offset_x = bounds[:x].negative? ? bounds[:x].abs : 0
136
- offset_y = bounds[:y].negative? ? bounds[:y].abs : 0
137
- total_width = bounds[:width] + offset_x
138
- total_height = bounds[:height] + offset_y
139
-
140
- <<~SVG
141
- <g style="fill:#{options[:background_color]};fill-opacity:1.00;">
142
- <rect x="0" y="0" width="#{total_width}" height="#{total_height}" shape-rendering="auto"/>
143
- </g>
144
- SVG
145
- end
146
-
147
- def grid_layer # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
148
- grid_size = 20
149
- grid_lines = +""
150
-
151
- # Vertical lines
152
- x = bounds[:x]
153
- while x <= bounds[:x] + bounds[:width]
154
- grid_lines << "<line x1=\"#{x}\" y1=\"#{bounds[:y]}\" " \
155
- "x2=\"#{x}\" " \
156
- "y2=\"#{bounds[:y] + bounds[:height]}\" " \
157
- "class=\"lutaml-diagram-grid\" />\n"
158
- x += grid_size
159
- end
160
-
161
- # Horizontal lines
162
- y = bounds[:y]
163
- while y <= bounds[:y] + bounds[:height]
164
- grid_lines << "<line x1=\"#{bounds[:x]}\" y1=\"#{y}\" " \
165
- "x2=\"#{bounds[:x] + bounds[:width]}\" " \
166
- "y2=\"#{y}\" class=\"lutaml-diagram-grid\" />\n"
167
- y += grid_size
168
- end
169
-
170
- "<g id=\"grid-layer\" " \
171
- "class=\"lutaml-diagram-grid-layer\">\n#{grid_lines}</g>\n"
172
- end
173
-
174
- def connectors_layer
175
- connectors_svg = diagram_renderer.connectors.map do |connector|
176
- render_connector(connector)
177
- end.join("\n")
178
-
179
- "<g id=\"connectors-layer\" " \
180
- "class=\"lutaml-diagram-connectors-layer\">\n" \
181
- "#{connectors_svg}\n</g>\n"
182
- end
183
-
184
- def elements_layer
185
- elements_svg = diagram_renderer.elements.map do |element|
186
- render_element(element)
187
- end.join("\n")
188
-
189
- "<g id=\"elements-layer\" " \
190
- "class=\"lutaml-diagram-elements-layer\">\n#{elements_svg}\n</g>\n"
191
- end
192
-
193
- def interactive_layer
194
- # Add interactive JavaScript if needed
195
- <<~SVG
196
- <script type="text/javascript">
197
- <![CDATA[
198
- // Basic interactivity
199
- document.addEventListener('DOMContentLoaded', function() {
200
- var elements = document.querySelectorAll('.lutaml-diagram-element');
201
- elements.forEach(function(el) {
202
- el.addEventListener('click', function(e) {
203
- var event = new CustomEvent('lutaml:element:click', {
204
- detail: {
205
- elementId: e.target.getAttribute('data-element-id'),
206
- elementType: e.target.getAttribute('data-element-type')
207
- }
208
- });
209
- document.dispatchEvent(event);
210
- console.log('Element clicked:', e.target.getAttribute('data-element-id'));
211
- });
212
- });
213
- });
214
- ]]>
215
- </script>
216
- SVG
217
- end
218
-
219
- def svg_footer
220
- "</svg>\n"
221
- end
222
-
223
- def render_connector(connector) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
224
- path_builder = PathBuilder.new(
225
- connector,
226
- connector[:source_element],
227
- connector[:target_element],
228
- )
229
- path_data = path_builder.build_path
230
-
231
- style = style_resolver.resolve_connector_style(connector)
232
-
233
- # Determine marker based on connector type
234
- markers = determine_marker_type(connector[:type])
235
- marker_start = markers[:start] || ""
236
- marker_end = markers[:end] || ""
237
-
238
- # Build style string
239
- style_attrs = []
240
- style_attrs << "stroke:#{style[:stroke] || '#000000'}"
241
- style_attrs << "stroke-width:#{style[:stroke_width] || '1'}"
242
- style_attrs << "stroke-linecap:#{style[:stroke_linecap] || 'round'}"
243
- style_attrs << "stroke-linejoin:#{style[:stroke_linejoin] || 'bevel'}"
244
- style_attrs << "fill:#{style[:fill] || 'none'}"
245
- style_attrs << "shape-rendering:#{style[:shape_rendering] || 'auto'}"
246
- if style[:stroke_dasharray]
247
- style_attrs << "stroke-dasharray:#{style[:stroke_dasharray]}"
248
- end
249
-
250
- <<~SVG
251
- <g style="#{style_attrs.join(';')}">
252
- <path d="#{path_data}"
253
- class="lutaml-diagram-connector lutaml-diagram-connector-#{connector[:type]}"
254
- data-connector-id="#{connector[:id]}"
255
- data-connector-type="#{connector[:type]}"
256
- #{"marker-start=\"#{marker_start}\"" unless marker_start.empty?}
257
- #{"marker-end=\"#{marker_end}\"" unless marker_end.empty?}
258
- shape-rendering="auto" />
259
- </g>
260
- SVG
261
- end
262
-
263
- def render_element(element)
264
- registry = ElementRenderers::DEFAULT_REGISTRY
265
- renderer_class = registry.renderer_for(element[:type]) ||
266
- ElementRenderers::BaseRenderer
267
-
268
- renderer = renderer_class.new(element, style_resolver)
269
- renderer.render
270
- end
271
-
272
- end
273
- end
274
- end
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ea
4
- module Diagram
5
- module Util
6
- # Convert a style hash to a CSS string
7
- #
8
- # @param style_hash [Hash] Style properties (e.g., { stroke: "#000" })
9
- # @return [String] CSS string (e.g., "stroke:#000;fill:none")
10
- def style_to_css(style_hash)
11
- style_hash.map { |k, v| "#{k}:#{v}" }.join(";")
12
- end
13
-
14
- # Parse geometry offsets
15
- def parse_geometry_offsets(geometry_string)
16
- geometry = parse_ea_geometry(geometry_string)
17
-
18
- [
19
- geometry[:source_offset_x].to_i,
20
- geometry[:source_offset_y].to_i,
21
- geometry[:target_offset_x].to_i,
22
- geometry[:target_offset_y].to_i,
23
- ]
24
- rescue TypeError, NoMethodError
25
- [0, 0, 0, 0]
26
- end
27
-
28
- def parse_ea_geometry(geometry_string) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
29
- return nil if geometry_string.nil? || geometry_string.strip.empty?
30
-
31
- data = {}
32
- begin
33
- geometry = geometry_string
34
- .gsub(/\s/, "")
35
- .downcase
36
- .split(";")
37
- .to_h { |pair| pair.split("=") }
38
-
39
- geometry.each do |k, v|
40
- if v.include?(",")
41
- # waypoints
42
- data[:waypoints] ||= []
43
- x_str, y_str = v.split(",")
44
- data[:waypoints] << { x: x_str.to_i, y: y_str.to_i }
45
- else
46
- key = case k
47
- when "sx"
48
- data[:has_relative_coords] ||= true
49
- "source_offset_x"
50
- when "sy"
51
- data[:has_relative_coords] ||= true
52
- "source_offset_y"
53
- when "ex"
54
- data[:has_relative_coords] ||= true
55
- "target_offset_x"
56
- when "ey"
57
- data[:has_relative_coords] ||= true
58
- "target_offset_y"
59
- else
60
- k
61
- end
62
-
63
- data[key.to_sym] = v.to_i
64
- end
65
- end
66
- rescue ArgumentError, TypeError
67
- data = {}
68
- end
69
- data
70
- end
71
- end
72
- end
73
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ea
4
- module Svg
5
- # Computes the actual drawing-area bounding box of an
6
- # Ea::Model::Diagram. EA stores bounds in pixel space with two
7
- # quirks: element rects can be inverted (rectbottom < recttop,
8
- # producing negative height), and elements/connectors can sit
9
- # outside the canvas bounds. The drawing area is the union of
10
- # all element rects and connector waypoint positions.
11
- class BoundsCalculator
12
- attr_reader :diagram
13
-
14
- def initialize(diagram)
15
- @diagram = diagram
16
- end
17
-
18
- def compute
19
- points = all_points
20
- return fallback_bounds if points.empty?
21
-
22
- xs = points.map(&:first)
23
- ys = points.map(&:last)
24
- Bounds.new(x: xs.min, y: ys.min, width: xs.max - xs.min,
25
- height: ys.max - ys.min)
26
- end
27
-
28
- private
29
-
30
- def all_points
31
- points = []
32
- diagram.elements.each do |elem|
33
- next unless elem.bounds
34
-
35
- b = normalize_bounds(elem.bounds)
36
- points << [b.x, b.y]
37
- points << [b.x + b.width, b.y + b.height]
38
- end
39
- diagram.connectors.each do |conn|
40
- conn.waypoints.each do |wp|
41
- next unless wp.position
42
-
43
- points << [wp.position.x, wp.position.y]
44
- end
45
- end
46
- points
47
- end
48
-
49
- # EA occasionally stores inverted rects (rectbottom < recttop).
50
- # Normalize so width/height are non-negative.
51
- def normalize_bounds(bounds)
52
- x = bounds.x
53
- y = bounds.y
54
- w = bounds.width
55
- h = bounds.height
56
- x, w = x + w, -w if w.negative?
57
- y, h = y + h, -h if h.negative?
58
- Bounds.new(x: x, y: y, width: w, height: h)
59
- end
60
-
61
- def fallback_bounds
62
- return Bounds.new(x: 0, y: 0, width: 1, height: 1) unless diagram.bounds
63
-
64
- Bounds.new(x: diagram.bounds.x, y: diagram.bounds.y,
65
- width: diagram.bounds.width.abs,
66
- height: diagram.bounds.height.abs)
67
- end
68
-
69
- # Internal value type — keep private to avoid polluting the
70
- # Ea::Svg namespace.
71
- Bounds = Struct.new(:x, :y, :width, :height, keyword_init: true)
72
- end
73
- end
74
- end
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ea
4
- module Svg
5
- # Renders one DiagramConnector as an SVG <path> through its
6
- # waypoints, with an arrowhead at the target end. Matches EA's
7
- # convention: filled triangle for navigable associations, filled
8
- # diamond for aggregations, open diamond for shared aggregations.
9
- class ConnectorPath
10
- ARROW_SIZE = 8
11
-
12
- attr_reader :connector, :relationship
13
-
14
- def initialize(connector, relationship: nil)
15
- @connector = connector
16
- @relationship = relationship
17
- end
18
-
19
- def render
20
- points = waypoints
21
- return "" if points.size < 2
22
-
23
- path_d = build_path_d(points)
24
- style = StyleResolver.new(connector.style)
25
-
26
- parts = []
27
- parts << %(<g class="connector" data-connector-id="#{escape(connector.id)}">)
28
- parts << %( <path d="#{path_d}" stroke="#{style.stroke_color}" stroke-width="#{style.stroke_width}" fill="none"/>)
29
- parts << render_source_marker(points) if source_marker?
30
- parts << render_target_marker(points)
31
- parts << %(</g>)
32
- parts.join("\n ")
33
- end
34
-
35
- private
36
-
37
- def waypoints
38
- connector.waypoints.filter_map do |wp|
39
- next unless wp.position
40
-
41
- [wp.position.x, wp.position.y]
42
- end
43
- end
44
-
45
- def build_path_d(points)
46
- moves = points.each_with_index.map do |p, idx|
47
- prefix = idx.zero? ? "M" : "L"
48
- "#{prefix} #{p[0]} #{p[1]}"
49
- end
50
- moves.join(" ")
51
- end
52
-
53
- def source_marker?
54
- return false unless relationship
55
-
56
- relationship.is_a?(Ea::Model::Association) &&
57
- relationship.source_aggregation != "none"
58
- end
59
-
60
- def render_source_marker(points)
61
- case relationship.source_aggregation
62
- when "composite" then filled_diamond(points.first, points[1])
63
- when "shared" then open_diamond(points.first, points[1])
64
- else ""
65
- end
66
- end
67
-
68
- def render_target_marker(points)
69
- filled_arrow(points.last, points[-2])
70
- end
71
-
72
- def filled_arrow(tip, base)
73
- return "" unless tip && base
74
-
75
- # Build a triangle pointing from base toward tip
76
- bx, by = base
77
- tx, ty = tip
78
- # Perpendicular vector for the wings
79
- dx = tx - bx
80
- dy = ty - by
81
- len = Math.sqrt(dx * dx + dy * dy)
82
- return "" if len.zero?
83
-
84
- ux = dx / len
85
- uy = dy / len
86
- # Wing points are ARROW_SIZE back from tip, perpendicular
87
- back_x = tx - ux * ARROW_SIZE
88
- back_y = ty - uy * ARROW_SIZE
89
- perp_x = -uy * (ARROW_SIZE / 2.0)
90
- perp_y = ux * (ARROW_SIZE / 2.0)
91
- w1_x = back_x + perp_x
92
- w1_y = back_y + perp_y
93
- w2_x = back_x - perp_x
94
- w2_y = back_y - perp_y
95
- points_str = "#{tx} #{ty} #{w1_x.round(1)} #{w1_y.round(1)} #{w2_x.round(1)} #{w2_y.round(1)}"
96
- %( <polygon points="#{points_str}" fill="black" stroke="black" stroke-width="1"/>)
97
- end
98
-
99
- def filled_diamond(tip, base)
100
- diamond_polygon(tip, base, fill: "black")
101
- end
102
-
103
- def open_diamond(tip, base)
104
- diamond_polygon(tip, base, fill: "white")
105
- end
106
-
107
- def diamond_polygon(tip, base, fill:)
108
- bx, by = base
109
- tx, ty = tip
110
- dx = tx - bx
111
- dy = ty - by
112
- len = Math.sqrt(dx * dx + dy * dy)
113
- return "" if len.zero?
114
-
115
- ux = dx / len
116
- uy = dy / len
117
- back_x = tx - ux * ARROW_SIZE
118
- back_y = ty - uy * ARROW_SIZE
119
- perp_x = -uy * (ARROW_SIZE / 2.0)
120
- perp_y = ux * (ARROW_SIZE / 2.0)
121
- far_x = tx - ux * (2 * ARROW_SIZE)
122
- far_y = ty - uy * (2 * ARROW_SIZE)
123
- w1_x = back_x + perp_x
124
- w1_y = back_y + perp_y
125
- w2_x = back_x - perp_x
126
- w2_y = back_y - perp_y
127
- points_str = "#{far_x.round(1)} #{far_y.round(1)} #{w1_x.round(1)} #{w1_y.round(1)} #{tx} #{ty} #{w2_x.round(1)} #{w2_y.round(1)}"
128
- %( <polygon points="#{points_str}" fill="#{fill}" stroke="black" stroke-width="1"/>)
129
- end
130
-
131
- def escape(text)
132
- return "" if text.nil?
133
-
134
- text.to_s.gsub("\"", "&quot;")
135
- end
136
- end
137
- end
138
- end