ea 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (548) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -1
  3. data/Rakefile +3 -0
  4. data/TODO.complete/01-t-xref-parsing.md +91 -0
  5. data/TODO.complete/02-per-label-geometry.md +50 -0
  6. data/TODO.complete/03-object-properties-constraints.md +57 -0
  7. data/TODO.complete/04-operation-params.md +37 -0
  8. data/TODO.complete/05-pdata-flag-matrix.md +52 -0
  9. data/TODO.complete/06-emf-image-rendering.md +34 -0
  10. data/TODO.complete/07-auxiliary-tables.md +47 -0
  11. data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
  12. data/TODO.complete/11-gml-xsd-generation.md +42 -0
  13. data/TODO.complete/13-shapescript-interpreter.md +28 -0
  14. data/TODO.complete/20-cli-diff.md +46 -0
  15. data/TODO.complete/21-cli-render.md +26 -0
  16. data/TODO.complete/22-cli-export.md +29 -0
  17. data/TODO.complete/23-cli-mdg.md +23 -0
  18. data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
  19. data/TODO.complete/31-public-send-cleanup.md +61 -0
  20. data/TODO.complete/32-spec-coverage.md +33 -0
  21. data/TODO.complete/33-blocked-parity-gaps.md +41 -0
  22. data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
  23. data/TODO.complete/37-cli-lint.md +32 -0
  24. data/TODO.complete/38-cli-query.md +26 -0
  25. data/TODO.complete/39-cli-info.md +23 -0
  26. data/TODO.complete/40-ocl-evaluator.md +31 -0
  27. data/TODO.complete/41-round-trip-spec.md +18 -0
  28. data/TODO.complete/42-html-diff-report.md +18 -0
  29. data/TODO.complete/43-json-schema-export.md +20 -0
  30. data/TODO.complete/44-remaining-qea-tables.md +40 -0
  31. data/TODO.complete/45-shapescript-extensions.md +25 -0
  32. data/TODO.complete/46-xsd-consolidation.md +20 -0
  33. data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
  34. data/TODO.complete/51-remove-respond-to.md +25 -0
  35. data/TODO.complete/52-xsd-decouple-fixtures.md +23 -0
  36. data/TODO.complete/53-xmi-public-send-cleanup.md +26 -0
  37. data/TODO.complete/54-wire-stereotype-icon-renderer.md +20 -0
  38. data/TODO.complete/55-wire-shapescript.md +20 -0
  39. data/TODO.complete/56-wire-emf-renderer.md +27 -0
  40. data/TODO.complete/57-wire-ocl-evaluator.md +23 -0
  41. data/TODO.complete/58-diff-modifications.md +21 -0
  42. data/TODO.complete/59-delete-toy-xmi-export.md +30 -0
  43. data/TODO.complete/60-curate-json-export.md +20 -0
  44. data/TODO.complete/61-plantuml-relationships.md +22 -0
  45. data/TODO.complete/62-changelog.md +17 -0
  46. data/TODO.complete/63-dependabot-fix.md +18 -0
  47. data/TODO.complete/STATUS.md +85 -0
  48. data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
  49. data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
  50. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  51. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  52. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  53. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  54. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  55. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  56. data/TODO.diagrams/12-connector-labels.md +40 -0
  57. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  58. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  59. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  60. data/TODO.diagrams/14-center-header-text.md +33 -0
  61. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  62. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  63. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  64. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  65. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  66. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  67. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  68. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  69. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  70. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  71. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  72. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  73. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  74. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  75. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  76. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  77. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  78. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  79. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  80. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  81. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  82. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  83. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  84. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  85. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  86. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  87. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  88. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  89. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  90. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  91. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  92. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  93. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  94. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  95. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  96. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  97. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  98. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  99. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  100. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  101. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  102. data/TODO.diagrams/50-examples-parity.md +85 -0
  103. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  104. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  105. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  106. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  107. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  108. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  109. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  110. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  111. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  112. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  113. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  114. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  115. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  116. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  117. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  118. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  119. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  120. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  121. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  122. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  123. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  124. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  125. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  126. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  127. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  128. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  129. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  130. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  131. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  132. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  133. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  134. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  135. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  136. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  137. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  138. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  139. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  140. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  141. data/TODO.diagrams/README.md +69 -0
  142. data/TODO.diagrams/STATUS.md +73 -0
  143. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  144. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  145. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  146. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  147. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  148. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  149. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  150. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  151. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  152. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  153. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  154. data/TODO.fixup/12-path-style-harmony.md +23 -0
  155. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  156. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  157. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  158. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  159. data/TODO.fixup/17-path-count-calibration.md +27 -0
  160. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  161. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  162. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  163. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  164. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  165. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  166. data/TODO.fixup/24-note-element-rendering.md +23 -0
  167. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  168. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  169. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  170. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  171. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  172. data/TODO.fixup/30-theme-system-stub.md +46 -0
  173. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  174. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  175. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  176. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  177. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  178. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  179. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  180. data/TODO.fixup/38-note-element-rendering.md +15 -0
  181. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  182. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  183. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  184. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  185. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  186. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  187. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  188. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  189. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  190. data/TODO.fixup/48-background-rect-format.md +15 -0
  191. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  192. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  193. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  194. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  195. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  196. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  197. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  198. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  199. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  200. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  201. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  202. data/TODO.fixup/60-cli-theme-option.md +22 -0
  203. data/TODO.fixup/61-theme-validation.md +28 -0
  204. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  205. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  206. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  207. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  208. data/TODO.fixup/66-styleex-writeback.md +22 -0
  209. data/TODO.fixup/67-delete-dead-code.md +28 -0
  210. data/TODO.fixup/68-figure-parity-status.md +82 -0
  211. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  212. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  213. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  214. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  215. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  216. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  217. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  218. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  219. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  220. data/TODO.fixup/78-note-element-rendering.md +30 -0
  221. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  222. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  223. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  224. data/config/qea_schema.yml +356 -0
  225. data/config/themes/119.yml +98 -0
  226. data/config/themes/default.yml +14 -0
  227. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  228. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  229. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  230. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  231. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  232. data/examples/exports/UmlModel_template/model.xml +23 -0
  233. data/examples/exports/basic/UML_EA.DTD +6856 -0
  234. data/examples/exports/basic/model.xml +8039 -0
  235. data/examples/exports/simple/UML_EA.DTD +6856 -0
  236. data/examples/exports/simple/model.xml +585 -0
  237. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  238. data/examples/exports/simple_example/model.xml +23 -0
  239. data/examples/exports/test/UML_EA.DTD +6856 -0
  240. data/examples/exports/test/model.xml +1638 -0
  241. data/examples/qea/basic.qea +0 -0
  242. data/frontend/README.md +51 -0
  243. data/frontend/dist/app.iife.js +24 -0
  244. data/frontend/dist/style.css +1 -0
  245. data/frontend/index.html +26 -0
  246. data/frontend/package-lock.json +2359 -0
  247. data/frontend/package.json +26 -0
  248. data/frontend/src/App.vue +59 -0
  249. data/frontend/src/app.ts +8 -0
  250. data/frontend/src/components/AppHeader.vue +127 -0
  251. data/frontend/src/components/AppSidebar.vue +119 -0
  252. data/frontend/src/components/ClassDetails.vue +247 -0
  253. data/frontend/src/components/DiagramView.vue +79 -0
  254. data/frontend/src/components/PackageDetails.vue +92 -0
  255. data/frontend/src/components/PackageTreeNode.vue +64 -0
  256. data/frontend/src/components/SearchResults.vue +48 -0
  257. data/frontend/src/components/WelcomeView.vue +87 -0
  258. data/frontend/src/shims-vue.d.ts +5 -0
  259. data/frontend/src/stores/dataStore.ts +79 -0
  260. data/frontend/src/stores/uiStore.ts +167 -0
  261. data/frontend/src/styles/main.css +1566 -0
  262. data/frontend/src/types.ts +221 -0
  263. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  264. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  265. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  266. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  267. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  268. data/frontend/tests/e2e/render.spec.ts +188 -0
  269. data/frontend/tsconfig.json +20 -0
  270. data/frontend/vite.config.ts +26 -0
  271. data/frontend/vitest.config.ts +8 -0
  272. data/lib/ea/cli/app.rb +57 -2
  273. data/lib/ea/cli/command/diagrams.rb +37 -28
  274. data/lib/ea/cli/command/diff.rb +46 -0
  275. data/lib/ea/cli/command/export.rb +101 -0
  276. data/lib/ea/cli/command/info.rb +75 -0
  277. data/lib/ea/cli/command/lint.rb +48 -0
  278. data/lib/ea/cli/command/mdg.rb +98 -0
  279. data/lib/ea/cli/command/query.rb +34 -0
  280. data/lib/ea/cli/command/render.rb +135 -0
  281. data/lib/ea/cli/command/svg.rb +8 -15
  282. data/lib/ea/cli/command.rb +7 -0
  283. data/lib/ea/diagram/display_config.rb +184 -0
  284. data/lib/ea/diagram.rb +4 -41
  285. data/lib/ea/diff/change.rb +30 -0
  286. data/lib/ea/diff/comparator.rb +109 -0
  287. data/lib/ea/diff/html_reporter.rb +91 -0
  288. data/lib/ea/diff.rb +15 -0
  289. data/lib/ea/export/json/generator.rb +118 -0
  290. data/lib/ea/export/json.rb +12 -0
  291. data/lib/ea/export/json_schema/generator.rb +77 -0
  292. data/lib/ea/export/json_schema.rb +12 -0
  293. data/lib/ea/export/plantuml/generator.rb +140 -0
  294. data/lib/ea/export/plantuml.rb +12 -0
  295. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  296. data/lib/ea/export/xsd/generator.rb +137 -0
  297. data/lib/ea/export/xsd/namespace_registry.rb +82 -0
  298. data/lib/ea/export/xsd.rb +19 -0
  299. data/lib/ea/export.rb +13 -0
  300. data/lib/ea/image/emf_renderer.rb +42 -0
  301. data/lib/ea/image.rb +12 -0
  302. data/lib/ea/lint/engine.rb +30 -0
  303. data/lib/ea/lint/lint_rule.rb +38 -0
  304. data/lib/ea/lint/offense.rb +18 -0
  305. data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
  306. data/lib/ea/lint/rules/duplicate_name.rb +28 -0
  307. data/lib/ea/lint/rules/missing_stereotype.rb +40 -0
  308. data/lib/ea/lint/rules/naming_convention.rb +44 -0
  309. data/lib/ea/lint/rules/orphan_element.rb +28 -0
  310. data/lib/ea/lint/rules.rb +14 -0
  311. data/lib/ea/lint.rb +13 -0
  312. data/lib/ea/mdg/document.rb +120 -0
  313. data/lib/ea/mdg/loader.rb +118 -0
  314. data/lib/ea/mdg/registry.rb +98 -0
  315. data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
  316. data/lib/ea/mdg/xml/applies_to.rb +18 -0
  317. data/lib/ea/mdg/xml/apply.rb +18 -0
  318. data/lib/ea/mdg/xml/content.rb +18 -0
  319. data/lib/ea/mdg/xml/documentation.rb +24 -0
  320. data/lib/ea/mdg/xml/stereotype.rb +28 -0
  321. data/lib/ea/mdg/xml/stereotypes.rb +18 -0
  322. data/lib/ea/mdg/xml/tag.rb +28 -0
  323. data/lib/ea/mdg/xml/tagged_values.rb +18 -0
  324. data/lib/ea/mdg/xml/technology.rb +28 -0
  325. data/lib/ea/mdg/xml/uml_profile.rb +22 -0
  326. data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
  327. data/lib/ea/mdg/xml.rb +24 -0
  328. data/lib/ea/mdg.rb +25 -0
  329. data/lib/ea/model/classifier.rb +4 -0
  330. data/lib/ea/model/constraint.rb +24 -0
  331. data/lib/ea/model/diagram.rb +85 -0
  332. data/lib/ea/model/diagram_connector.rb +34 -0
  333. data/lib/ea/model/diagram_element.rb +16 -0
  334. data/lib/ea/model/document.rb +47 -2
  335. data/lib/ea/model/ghost_label.rb +32 -0
  336. data/lib/ea/model/instance_specification.rb +34 -0
  337. data/lib/ea/model/legend.rb +43 -0
  338. data/lib/ea/model/legend_item.rb +26 -0
  339. data/lib/ea/model/note.rb +28 -0
  340. data/lib/ea/model/operation.rb +2 -0
  341. data/lib/ea/model/property.rb +2 -0
  342. data/lib/ea/model/relationship.rb +2 -0
  343. data/lib/ea/model/signal.rb +14 -0
  344. data/lib/ea/model/slot.rb +19 -0
  345. data/lib/ea/model.rb +8 -0
  346. data/lib/ea/ocl/evaluator.rb +94 -0
  347. data/lib/ea/ocl/nodes.rb +30 -0
  348. data/lib/ea/ocl/parser.rb +133 -0
  349. data/lib/ea/ocl/unsupported_error.rb +7 -0
  350. data/lib/ea/ocl.rb +13 -0
  351. data/lib/ea/qea/factory/document_builder.rb +24 -10
  352. data/lib/ea/qea/factory/generalization_builder.rb +9 -11
  353. data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
  354. data/lib/ea/qea/models/ea_author.rb +35 -0
  355. data/lib/ea/qea/models/ea_glossary.rb +34 -0
  356. data/lib/ea/qea/models/ea_image.rb +53 -0
  357. data/lib/ea/qea/models/ea_implement.rb +32 -0
  358. data/lib/ea/qea/models/ea_list.rb +42 -0
  359. data/lib/ea/qea/models/ea_object_effort.rb +34 -0
  360. data/lib/ea/qea/models/ea_object_problem.rb +37 -0
  361. data/lib/ea/qea/models/ea_object_require.rb +34 -0
  362. data/lib/ea/qea/models/ea_object_resource.rb +34 -0
  363. data/lib/ea/qea/models/ea_object_risk.rb +34 -0
  364. data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
  365. data/lib/ea/qea/models/ea_object_test.rb +35 -0
  366. data/lib/ea/qea/models/ea_object_trx.rb +34 -0
  367. data/lib/ea/qea/models/ea_operation.rb +7 -0
  368. data/lib/ea/qea/models/ea_palette.rb +30 -0
  369. data/lib/ea/qea/models/ea_palette_item.rb +36 -0
  370. data/lib/ea/qea/models/ea_phase.rb +40 -0
  371. data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
  372. data/lib/ea/qea/models/ea_secrypt.rb +34 -0
  373. data/lib/ea/qea/models/ea_version.rb +39 -0
  374. data/lib/ea/qea/models/ea_xref.rb +11 -1
  375. data/lib/ea/qea/models.rb +19 -0
  376. data/lib/ea/qea/services/database_loader.rb +22 -1
  377. data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +65 -0
  378. data/lib/ea/qea/validation/database.rb +2 -0
  379. data/lib/ea/qea/validation/validation_engine.rb +2 -0
  380. data/lib/ea/qea/validation.rb +2 -0
  381. data/lib/ea/query/builder.rb +127 -0
  382. data/lib/ea/query.rb +9 -0
  383. data/lib/ea/render/image_converter.rb +101 -0
  384. data/lib/ea/render.rb +10 -0
  385. data/lib/ea/shapescript/parser.rb +208 -0
  386. data/lib/ea/shapescript/renderer.rb +74 -0
  387. data/lib/ea/shapescript/shape.rb +34 -0
  388. data/lib/ea/shapescript.rb +24 -0
  389. data/lib/ea/sources/qea/adapter.rb +22 -5
  390. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  391. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  392. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  393. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  394. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  395. data/lib/ea/sources/qea/note_builder.rb +58 -0
  396. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  397. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  398. data/lib/ea/sources/qea/package_builder.rb +16 -0
  399. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  400. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  401. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  402. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  403. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  404. data/lib/ea/sources/qea/xref/record.rb +41 -0
  405. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  406. data/lib/ea/sources/qea/xref.rb +27 -0
  407. data/lib/ea/sources/qea.rb +4 -0
  408. data/lib/ea/sources/xmi/adapter.rb +80 -2
  409. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  410. data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
  411. data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
  412. data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
  413. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  414. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  415. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  416. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  417. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  418. data/lib/ea/sources/xmi.rb +4 -0
  419. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  420. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  421. data/lib/ea/spa/output/strategy.rb +34 -0
  422. data/lib/ea/svg/connector_router.rb +134 -0
  423. data/lib/ea/svg/ea_emitter/background.rb +2 -3
  424. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  425. data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
  426. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  427. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  428. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  429. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  430. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  431. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  432. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  433. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  434. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  435. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  436. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  437. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  438. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  439. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  440. data/lib/ea/svg/ea_emitter/compartment/stereotype_icon.rb +32 -0
  441. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  442. data/lib/ea/svg/ea_emitter/compartment.rb +60 -0
  443. data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
  444. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  445. data/lib/ea/svg/ea_emitter/document.rb +49 -25
  446. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  447. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  448. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  449. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  450. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  451. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  452. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  453. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  454. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  455. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  456. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  457. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  458. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  459. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  460. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  461. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  462. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  463. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  464. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  465. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  466. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  467. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  468. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  469. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  470. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +119 -0
  471. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  472. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  473. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  474. data/lib/ea/svg/ea_emitter/elements.rb +283 -95
  475. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  476. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  477. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  478. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  479. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  480. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  481. data/lib/ea/svg/ea_emitter/labels.rb +93 -27
  482. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  483. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  484. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  485. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  486. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  487. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  488. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  489. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  490. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  491. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  492. data/lib/ea/svg/ea_emitter/markers.rb +351 -64
  493. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  494. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  495. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  496. data/lib/ea/svg/ea_emitter.rb +33 -11
  497. data/lib/ea/svg/parity/checker.rb +105 -0
  498. data/lib/ea/svg/parity/source.rb +59 -0
  499. data/lib/ea/svg/parity/suite.rb +113 -0
  500. data/lib/ea/svg/parity.rb +14 -0
  501. data/lib/ea/svg.rb +2 -15
  502. data/lib/ea/theme/definition.rb +176 -0
  503. data/lib/ea/theme/loader.rb +125 -0
  504. data/lib/ea/theme/registry.rb +96 -0
  505. data/lib/ea/theme.rb +37 -0
  506. data/lib/ea/transformations.rb +5 -88
  507. data/lib/ea/validation/xmi_parity.rb +80 -0
  508. data/lib/ea/validation.rb +12 -0
  509. data/lib/ea/version.rb +4 -1
  510. data/lib/ea.rb +15 -4
  511. data/lib/tasks/svg_bench.rake +87 -0
  512. data/lutaml-uml-0.5.1.gem +0 -0
  513. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  514. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  515. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  516. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  517. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  518. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  519. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  520. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  521. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  522. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  523. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  524. metadata +464 -26
  525. data/lib/ea/diagram/configuration.rb +0 -379
  526. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  527. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  528. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  529. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  530. data/lib/ea/diagram/element_renderers.rb +0 -43
  531. data/lib/ea/diagram/extractor.rb +0 -566
  532. data/lib/ea/diagram/layout_engine.rb +0 -170
  533. data/lib/ea/diagram/path_builder.rb +0 -202
  534. data/lib/ea/diagram/style_parser.rb +0 -42
  535. data/lib/ea/diagram/style_resolver.rb +0 -291
  536. data/lib/ea/diagram/svg_renderer.rb +0 -274
  537. data/lib/ea/diagram/util.rb +0 -73
  538. data/lib/ea/svg/bounds_calculator.rb +0 -74
  539. data/lib/ea/svg/connector_path.rb +0 -138
  540. data/lib/ea/svg/element_box.rb +0 -256
  541. data/lib/ea/svg/renderer.rb +0 -77
  542. data/lib/ea/svg/style_resolver.rb +0 -72
  543. data/lib/ea/transformations/configuration.rb +0 -333
  544. data/lib/ea/transformations/format_registry.rb +0 -366
  545. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  546. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  547. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  548. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Svg
5
+ module EaEmitter
6
+ module Compartment
7
+ # Package-contents compartment. Renders the package's child
8
+ # classifiers and sub-packages as a list of "+ Name" rows in
9
+ # the body, matching EA's package-on-diagram rendering. Each
10
+ # row also draws a small per-type icon to the left of the
11
+ # text (Enumeration gets a list-style icon; other classifiers
12
+ # get the "folded paper" silhouette).
13
+ module PackageContents
14
+ ROW_HEIGHT = 16
15
+ ICON_X_OFFSET = 5
16
+ TEXT_X_OFFSET = 23
17
+
18
+ module_function
19
+
20
+ def render(context)
21
+ rows = context.package_content_lines
22
+ return nil if rows.nil? || rows.empty?
23
+
24
+ bounds = context.bounds
25
+ first_y = context.geometry.attr_first_y
26
+ blocks = rows.each_with_index.flat_map do |row, idx|
27
+ y_top = first_y - 11 + (idx * ROW_HEIGHT)
28
+ y_text = first_y + (idx * ROW_HEIGHT)
29
+ [
30
+ icon_block(bounds, y_top, row.kind),
31
+ text_block(bounds, y_text, row.name, context)
32
+ ]
33
+ end
34
+ wrap_group(blocks.join("\n"))
35
+ end
36
+
37
+ def wrap_group(body)
38
+ %(<g style="#{group_style}">\n#{body}\n</g>)
39
+ end
40
+ module_function :wrap_group
41
+
42
+ def group_style
43
+ "stroke-width:1;stroke-linecap:square;stroke-linejoin:bevel; " \
44
+ "fill-opacity:1.00; stroke-opacity:1.00"
45
+ end
46
+ module_function :group_style
47
+
48
+ def icon_block(bounds, y_top, kind)
49
+ Element::RowIconRenderer.render(
50
+ x_pos: bounds.x + ICON_X_OFFSET,
51
+ y_pos: y_top,
52
+ kind: kind || :default
53
+ )
54
+ end
55
+ module_function :icon_block
56
+
57
+ def text_block(bounds, y, name, context)
58
+ TextRenderer.new(
59
+ content: "+ #{name}",
60
+ x: bounds.x + TEXT_X_OFFSET,
61
+ y: y,
62
+ family: context.family, size: context.size,
63
+ size_unit: context.size_unit,
64
+ fill: context.theme.attribute_text_color
65
+ ).to_svg
66
+ end
67
+ module_function :text_block
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Svg
5
+ module EaEmitter
6
+ module Compartment
7
+ # Renders the "(from ParentPackage)" italic subtitle inside
8
+ # a Package element's body. EA emits this when a package is
9
+ # placed on a diagram whose own package is NOT the package's
10
+ # parent — the subtitle shows the package's actual parent
11
+ # context so the reader can locate it in the model tree.
12
+ #
13
+ # Format: italic, small font, centered horizontally in the
14
+ # element body, positioned near the bottom of the body.
15
+ module PackageFromParent
16
+ Y_OFFSET_FROM_BOTTOM = 5
17
+
18
+ module_function
19
+
20
+ def render(context)
21
+ pkg = context.model_element
22
+ return nil unless pkg.is_a?(Ea::Model::Package)
23
+
24
+ parent_name = parent_name_for(pkg, context)
25
+ return nil if parent_name.nil? || parent_name.empty?
26
+
27
+ subtitle = "(from #{parent_name})"
28
+ bounds = context.bounds
29
+ fill = context.theme.attribute_text_color
30
+ y = bounds.y + bounds.height - Y_OFFSET_FROM_BOTTOM
31
+ estimated_width = subtitle.length * context.size * 0.5
32
+ x = bounds.x + (bounds.width - estimated_width) / 2.0
33
+ text = TextRenderer.new(
34
+ content: subtitle, x: x, y: y,
35
+ family: context.family, size: context.size,
36
+ size_unit: context.size_unit, fill: fill,
37
+ style: "italic"
38
+ ).to_svg
39
+ wrap(text, fill)
40
+ end
41
+
42
+ # Resolves the package's parent's name via the model index.
43
+ # Returns nil when:
44
+ # - the package has no parent
45
+ # - the parent IS the diagram's own package (already in context)
46
+ # - the parent is ALSO placed on the current diagram
47
+ # - every placed Package on the diagram shares the same
48
+ # parent_id (diagram is implicitly about that parent's
49
+ # children — EA omits the subtitle as redundant)
50
+ # - the parent's name equals the placed package's name
51
+ # (e.g. an inner "3D都市モデル" nested inside an outer
52
+ # "3D都市モデル" — the subtitle would be tautological)
53
+ # - the diagram's name contains any placed Package's name
54
+ # as a substring (the diagram is implicitly "about" that
55
+ # package — subtitles would be redundant). Verified
56
+ # against plateau's "3D都市モデル応用スキーマと他のスキーマとの関係"
57
+ # diagram, which contains the placed name "3D都市モデル"
58
+ # and renders 0 subtitles in EA's reference.
59
+ def parent_name_for(pkg, context)
60
+ parent_id = pkg.parent_id
61
+ return nil if parent_id.nil? || parent_id.empty?
62
+ return nil if parent_id == context.diagram&.package_id
63
+ return nil if parent_on_diagram?(parent_id, context)
64
+ return nil if uniform_package_parent?(context)
65
+ return nil if diagram_name_contains_placed_package?(context)
66
+
67
+ parent = context.model_index_for(parent_id)
68
+ return nil unless parent.is_a?(Ea::Model::Package)
69
+ return nil if parent.name.to_s == pkg.name.to_s
70
+
71
+ parent.name.to_s.empty? ? nil : parent.name
72
+ end
73
+ module_function :parent_name_for
74
+
75
+ # Returns true when the diagram has 2+ placed Packages and
76
+ # every placed Package shares the same parent_id. EA treats
77
+ # such diagrams as "children of X" overviews and suppresses
78
+ # the (from X) subtitle on every element.
79
+ def uniform_package_parent?(context)
80
+ diagram = context.diagram
81
+ return false unless diagram && context.model_index
82
+
83
+ placed_pkg_parents = (diagram.elements || []).filter_map do |el|
84
+ me = context.model_index[el.model_element_ref]
85
+ next nil unless me.is_a?(Ea::Model::Package)
86
+
87
+ me.parent_id
88
+ end
89
+ placed_pkg_parents.size > 1 && placed_pkg_parents.uniq.size == 1
90
+ end
91
+ module_function :uniform_package_parent?
92
+
93
+ def parent_on_diagram?(parent_id, context)
94
+ diagram = context.diagram
95
+ return false unless diagram
96
+
97
+ (diagram.elements || []).any? do |e|
98
+ e.model_element_ref == parent_id
99
+ end
100
+ end
101
+ module_function :parent_on_diagram?
102
+
103
+ # Returns true when the diagram's name contains any placed
104
+ # Package's name as a substring. EA omits all subtitles in
105
+ # such diagrams — the name itself signals which package is
106
+ # the subject, making subtitles redundant.
107
+ def diagram_name_contains_placed_package?(context)
108
+ diagram = context.diagram
109
+ return false unless diagram && context.model_index
110
+
111
+ name = diagram.name.to_s
112
+ return false if name.empty?
113
+
114
+ (diagram.elements || []).any? do |el|
115
+ me = context.model_index[el.model_element_ref]
116
+ next false unless me.is_a?(Ea::Model::Package)
117
+
118
+ pkg_name = me.name.to_s
119
+ !pkg_name.empty? && name.include?(pkg_name)
120
+ end
121
+ end
122
+ module_function :diagram_name_contains_placed_package?
123
+
124
+ def wrap(body, fill)
125
+ group_style = "stroke-width:1;stroke-linecap:round;" \
126
+ "stroke-linejoin:bevel; fill:#{fill};" \
127
+ "fill-opacity:1.00; stroke:#000000;" \
128
+ " stroke-opacity:0.00"
129
+ %(<g style="#{group_style}">\n#{body}\n</g>)
130
+ end
131
+ module_function :wrap
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Svg
5
+ module EaEmitter
6
+ module Compartment
7
+ # Element shape: stroked rect for classifiers, folder
8
+ # silhouette for packages, note silhouette for notes.
9
+ module Shape
10
+ module_function
11
+
12
+ def render(context)
13
+ if legend?(context)
14
+ Element::LegendRenderer.render(
15
+ context.bounds,
16
+ legend: context.model_element.legend,
17
+ family: context.family,
18
+ canvas: context.canvas
19
+ )
20
+ elsif context.model_element.is_a?(Ea::Model::Package) ||
21
+ context.model_element.is_a?(Ea::Model::Note)
22
+ label = package_label(context.model_element)
23
+ stereotype = package_stereotype(context.model_element)
24
+ Element::PackageShapeRenderer.render(
25
+ context.bounds,
26
+ fill: context.fill, stroke: context.stroke,
27
+ stroke_width: context.stroke_width,
28
+ label: label,
29
+ stereotype: stereotype,
30
+ family: context.family, size: context.size,
31
+ size_unit: context.size_unit,
32
+ text_fill: context.text_fill
33
+ )
34
+ elsif hand_draw?(context)
35
+ Element::HandDrawShapeRenderer.render(
36
+ context.bounds,
37
+ fill: context.fill, stroke: context.stroke,
38
+ stroke_width: context.stroke_width
39
+ )
40
+ else
41
+ Element::ShapeRenderer.render(
42
+ context.bounds,
43
+ fill: context.fill, stroke: context.stroke,
44
+ stroke_width: context.stroke_width
45
+ )
46
+ end
47
+ end
48
+
49
+ # A Text element with LegendOpts= renders as the legend
50
+ # block rather than a folder silhouette. Dispatch on the
51
+ # presence of the attached Legend payload.
52
+ def legend?(context)
53
+ context.model_element.is_a?(Ea::Model::Note) &&
54
+ context.model_element.legend
55
+ end
56
+
57
+ # HandDraw=1 in the diagram StyleEx switches every element
58
+ # to EA's hand-drawn wavy silhouette.
59
+ def hand_draw?(context)
60
+ context.diagram && context.diagram.hand_draw
61
+ end
62
+
63
+ def package_label(model_element)
64
+ model_element.name.to_s
65
+ end
66
+
67
+ # EA renders a package's applied stereotype above the name
68
+ # inside the tab area when one is set. Returns nil when no
69
+ # stereotype is applied (the common case).
70
+ def package_stereotype(model_element)
71
+ return nil unless model_element.is_a?(Ea::Model::Package)
72
+
73
+ refs = model_element.stereotype_refs
74
+ return nil unless refs&.any?
75
+
76
+ refs.first.to_s
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Svg
5
+ module EaEmitter
6
+ module Compartment
7
+ # Stereotype decorator icon compartment. Emits the small
8
+ # symbolic polygon that EA renders inside an element's body
9
+ # when a stereotype provides a custom icon (FeatureType
10
+ # diamond, Type polygon, etc.). Delegates to
11
+ # Element::StereotypeIconRenderer.
12
+ #
13
+ # Rendered after the header so the icon sits inside the
14
+ # element body below the stereotype label.
15
+ module StereotypeIcon
16
+ module_function
17
+
18
+ def render(context)
19
+ return nil unless context.classifier
20
+
21
+ svg = Element::StereotypeIconRenderer.render(
22
+ classifier: context.classifier,
23
+ bounds: context.bounds,
24
+ canvas: context.canvas
25
+ )
26
+ svg.empty? ? nil : svg
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Svg
5
+ module EaEmitter
6
+ module Compartment
7
+ # Tagged value compartment. Skipped when no tagged values
8
+ # or when geometry cannot fit them.
9
+ module TaggedValues
10
+ DEFAULT_TEXT_COLOR = "#000000"
11
+
12
+ module_function
13
+
14
+ def render(context)
15
+ return nil unless context.tagged_values.any?
16
+ return nil unless context.geometry.tagged_value_first_y
17
+
18
+ Element::TaggedValueRenderer.render(
19
+ context.tagged_values,
20
+ bounds: context.bounds,
21
+ first_y: context.geometry.tagged_value_first_y,
22
+ family: context.family, size: context.size,
23
+ fill: DEFAULT_TEXT_COLOR
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Svg
5
+ module EaEmitter
6
+ # Compartment pipeline. Each Compartment is a small,
7
+ # single-purpose renderer that decides whether it applies to
8
+ # the current RenderContext and, if so, emits its SVG layer.
9
+ #
10
+ # Adding a new compartment = adding a class to `COMPARTMENTS`.
11
+ # The Elements orchestrator does not change — OCP-friendly.
12
+ module Compartment
13
+ autoload :Shape, "ea/svg/ea_emitter/compartment/shape"
14
+ autoload :NoteBody, "ea/svg/ea_emitter/compartment/note_body"
15
+ autoload :Header, "ea/svg/ea_emitter/compartment/header"
16
+ autoload :HeaderDivider, "ea/svg/ea_emitter/compartment/header_divider"
17
+ autoload :StereotypeIcon, "ea/svg/ea_emitter/compartment/stereotype_icon"
18
+ autoload :Attributes, "ea/svg/ea_emitter/compartment/attributes"
19
+ autoload :Operations, "ea/svg/ea_emitter/compartment/operations"
20
+ autoload :EnumLiterals, "ea/svg/ea_emitter/compartment/enum_literals"
21
+ autoload :TaggedValues, "ea/svg/ea_emitter/compartment/tagged_values"
22
+ autoload :Constraints, "ea/svg/ea_emitter/compartment/constraints"
23
+ autoload :PackageContents, "ea/svg/ea_emitter/compartment/package_contents"
24
+ autoload :InstanceUnderline, "ea/svg/ea_emitter/compartment/instance_underline"
25
+ autoload :InstanceSlots, "ea/svg/ea_emitter/compartment/instance_slots"
26
+ autoload :NoteText, "ea/svg/ea_emitter/compartment/note_text"
27
+ autoload :PackageFromParent, "ea/svg/ea_emitter/compartment/package_from_parent"
28
+
29
+ # Order matters: EA emits compartments top-to-bottom inside
30
+ # each element group.
31
+ ALL = [
32
+ Shape,
33
+ NoteBody,
34
+ Header,
35
+ HeaderDivider,
36
+ StereotypeIcon,
37
+ Attributes,
38
+ Operations,
39
+ EnumLiterals,
40
+ TaggedValues,
41
+ Constraints,
42
+ PackageContents,
43
+ InstanceSlots,
44
+ NoteText,
45
+ PackageFromParent,
46
+ InstanceUnderline
47
+ ].freeze
48
+
49
+ module_function
50
+
51
+ # Renders every applicable compartment for the context.
52
+ # Returns an Array of SVG strings (some may be nil; the
53
+ # caller compacts).
54
+ def render_all(context)
55
+ ALL.map { |compartment| compartment.render(context) }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -3,28 +3,55 @@
3
3
  module Ea
4
4
  module Svg
5
5
  module EaEmitter
6
- # Emits the connectors layer: for each DiagramConnector, emits
7
- # one <g> containing the <path d="M x y L x2 y2..."/> through
8
- # its waypoints. Markers (arrowheads, diamonds) are emitted
9
- # separately by the Markers emitter.
6
+ # Emits connector line `<path>` elements grouped per line-style.
7
+ # Returns an Array of `Layer` structs.
8
+ #
9
+ # Default mode (`grouped: true`) consolidates all same-style
10
+ # paths into ONE `<g>` element — matching EA's encoding for
11
+ # diagrams with many similarly-styled connectors.
12
+ #
13
+ # Pass `grouped: false` for per-connector grouping (one `<g>`
14
+ # per line, used when strict per-entity interleaving with
15
+ # markers is required).
10
16
  class Connectors
11
- attr_reader :diagram
17
+ LINE_STYLE_KEY = :connector_line
12
18
 
13
- def initialize(diagram)
19
+ attr_reader :diagram, :canvas, :grouped, :stroke_width
20
+
21
+ def initialize(diagram, canvas: nil, grouped: true, stroke_width: 2)
14
22
  @diagram = diagram
23
+ @canvas = canvas
24
+ @grouped = grouped
25
+ @stroke_width = stroke_width
15
26
  end
16
27
 
17
- def render
28
+ def layers
18
29
  paths = visible_connectors.filter_map { |c| path_for(c) }
19
- return "" if paths.empty?
30
+ return [] if paths.empty?
31
+
32
+ if grouped
33
+ [Layer.new(style_key: LINE_STYLE_KEY, style: line_style,
34
+ body: paths.join("\n "))]
35
+ else
36
+ paths.map do |path|
37
+ Layer.new(style_key: LINE_STYLE_KEY, style: line_style, body: path)
38
+ end
39
+ end
40
+ end
20
41
 
21
- %(<g style="stroke-width:2;stroke-linecap:round;stroke-linejoin:bevel; fill:#000000;fill-opacity:0.00; stroke:#000000; stroke-opacity:1.00">\n#{paths.join("\n")}\n</g>)
42
+ def line_style
43
+ "stroke-width:#{stroke_width};stroke-linecap:round;stroke-linejoin:bevel; fill:#000000;fill-opacity:0.00; stroke:#000000; stroke-opacity:1.00"
44
+ end
45
+
46
+ # Backwards-compatible render — joins all layers' `<g>`.
47
+ def render
48
+ layers.map(&:to_svg).join("\n")
22
49
  end
23
50
 
24
51
  private
25
52
 
26
53
  def visible_connectors
27
- (diagram.connectors || []).reject(&:hidden)
54
+ (diagram.connectors || []).select(&:renderable?)
28
55
  end
29
56
 
30
57
  def path_for(connector)
@@ -32,9 +59,10 @@ module Ea
32
59
  return nil if pts.size < 2
33
60
 
34
61
  d = pts.each_with_index.map do |p, idx|
35
- "#{idx.zero? ? 'M' : 'L'} #{p[0]} #{p[1]}"
62
+ x, y = translate_point(p)
63
+ "#{idx.zero? ? 'M' : 'L'} #{Canvas.coord(x)} #{Canvas.coord(y)}"
36
64
  end.join(" ")
37
- %( <path d="#{d}" shape-rendering="auto"/>)
65
+ %(<path d="#{d}" shape-rendering="auto"/>)
38
66
  end
39
67
 
40
68
  def waypoints_for(connector)
@@ -44,6 +72,12 @@ module Ea
44
72
  [wp.position.x, wp.position.y]
45
73
  end
46
74
  end
75
+
76
+ def translate_point(p)
77
+ return p unless @canvas
78
+
79
+ [@canvas.translate_x(p[0]), @canvas.translate_y(p[1])]
80
+ end
47
81
  end
48
82
  end
49
83
  end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Svg
5
+ module EaEmitter
6
+ # Emits the diagram frame: outer border + name tab. EA renders
7
+ # every diagram with a 1px black border forming a rectangle
8
+ # inset 6px from the canvas edge, plus a "tab" polygon in the
9
+ # upper-left containing the diagram type + name text.
10
+ #
11
+ # Layout:
12
+ # ┌───┐
13
+ # │tab│──────────────────┐
14
+ # └───┘ │
15
+ # │
16
+ # (diagram contents) │
17
+ # │
18
+ # ┌──────────────────────┘
19
+ #
20
+ # Format observed in EA reference SVGs:
21
+ # <g style="...stroke-width:1;stroke-linecap:square;
22
+ # stroke-linejoin:miter; fill:#000000;
23
+ # fill-opacity:0.00; stroke:#000000;
24
+ # stroke-opacity:1.00">
25
+ # <path d="M 6 6 L 6 H-6 W-6 W-6 6 L 6 6"/>
26
+ # </g>
27
+ # <g style="...fill:#FFFFFF;...">
28
+ # <polygon points="6 26 X 26 X+13 12 X+13 6 6 6 6 26"/>
29
+ # </g>
30
+ # <g style="...">
31
+ # <text x="11" y="19" ...>class DiagramName</text>
32
+ # </g>
33
+ class DiagramFrame
34
+ attr_reader :canvas, :theme
35
+
36
+ def initialize(canvas:, theme: nil)
37
+ @canvas = canvas
38
+ @theme = theme || Ea::Theme::Registry.default
39
+ end
40
+
41
+ def layers(diagram)
42
+ return [] if canvas.nil?
43
+
44
+ [
45
+ border_layer(canvas),
46
+ tab_layer(canvas, diagram),
47
+ tab_label_layer(canvas, diagram)
48
+ ].compact
49
+ end
50
+
51
+ private
52
+
53
+ def border_layer(canvas)
54
+ d = border_path_d(canvas)
55
+ body = %(<path d="#{d}" shape-rendering="auto"/>)
56
+ Layer.new(style_key: :frame_border, style: BORDER_STYLE, body: body)
57
+ end
58
+
59
+ def tab_layer(canvas, diagram)
60
+ points = tab_points(canvas, diagram)
61
+ body = %(<polygon points="#{points}" shape-rendering="auto" style="fill-rule:evenodd;"/>)
62
+ Layer.new(style_key: :frame_tab, style: TAB_STYLE, body: body)
63
+ end
64
+
65
+ def tab_label_layer(canvas, diagram)
66
+ label = tab_label(diagram)
67
+ return nil unless label
68
+
69
+ body = build_label_text(label)
70
+ Layer.new(style_key: :frame_label, style: LABEL_STYLE, body: body)
71
+ end
72
+
73
+ def border_path_d(canvas)
74
+ inset = theme.frame.inset
75
+ right = canvas.width - inset
76
+ bottom = canvas.height - inset
77
+ "M #{inset} #{inset} L #{inset} #{bottom} L #{right} #{bottom} L #{right} #{inset} L #{inset} #{inset}"
78
+ end
79
+
80
+ def tab_points(canvas, diagram)
81
+ label_width = tab_label_width(diagram)
82
+ x1 = theme.frame.inset
83
+ x2 = x1 + label_width
84
+ x3 = x2 + theme.frame.tab_slant
85
+ top1 = theme.frame.inset
86
+ top2 = theme.frame.inset + theme.frame.tab_height
87
+ "#{x1} #{top2} #{x2} #{top2} #{x3} #{top1 + theme.frame.tab_height - theme.frame.tab_slant} #{x3} #{top1} #{x1} #{top1} #{x1} #{top2}"
88
+ end
89
+
90
+ def tab_label(diagram)
91
+ type = diagram_label_prefix(diagram)
92
+ return nil unless type
93
+
94
+ "#{type} #{diagram.name}"
95
+ end
96
+
97
+ def diagram_label_prefix(diagram)
98
+ case diagram.diagram_type&.downcase
99
+ when "logical" then "class"
100
+ when "package" then "pkg"
101
+ when "usecase" then "uc"
102
+ when "sequence" then "seq"
103
+ when "activity" then "act"
104
+ when "statechart" then "sm"
105
+ when "deployment" then "dep"
106
+ when "component" then "comp"
107
+ when "object" then "object"
108
+ when "custom", "conceptual" then nil
109
+ else diagram.diagram_type&.downcase
110
+ end
111
+ end
112
+
113
+ def tab_label_width(diagram)
114
+ label = tab_label(diagram)
115
+ return theme.package.default_tab_width unless label
116
+
117
+ text_width = Ea::Svg::EaEmitter::TextRenderer.estimate_width(
118
+ label, theme.font_size || 7, theme.text_width_factor
119
+ ).round
120
+ text_width + theme.frame.tab_padding
121
+ end
122
+
123
+ def build_label_text(label)
124
+ TextRenderer.new(
125
+ content: label,
126
+ x: theme.frame.tab_label_x, y: theme.frame.tab_label_y,
127
+ family: theme.font_family || "Calibri",
128
+ size: theme.font_size || 7,
129
+ weight: theme.text_weight_bold,
130
+ size_unit: theme.font_size_unit,
131
+ fill: "#000000",
132
+ stroke_in_text: theme.stroke_in_text_color,
133
+ width_factor: theme.text_width_factor
134
+ ).to_svg
135
+ end
136
+
137
+ BORDER_STYLE = "stroke-width:1;stroke-linecap:square;stroke-linejoin:miter; fill:#000000;fill-opacity:0.00; stroke:#000000; stroke-opacity:1.00"
138
+ TAB_STYLE = "stroke-width:1;stroke-linecap:square;stroke-linejoin:miter; fill:#FFFFFF;fill-opacity:1.00; stroke:#000000; stroke-opacity:1.00"
139
+ LABEL_STYLE = "stroke-width:1;stroke-linecap:square;stroke-linejoin:miter; fill:#000000;fill-opacity:1.00; stroke:#000000; stroke-opacity:0.00"
140
+ end
141
+ end
142
+ end
143
+ end