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.
- checksums.yaml +4 -4
- data/Rakefile +3 -0
- data/TODO.complete/01-t-xref-parsing.md +91 -0
- data/TODO.complete/02-per-label-geometry.md +50 -0
- data/TODO.complete/03-object-properties-constraints.md +57 -0
- data/TODO.complete/04-operation-params.md +37 -0
- data/TODO.complete/05-pdata-flag-matrix.md +52 -0
- data/TODO.complete/06-emf-image-rendering.md +34 -0
- data/TODO.complete/07-auxiliary-tables.md +47 -0
- data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
- data/TODO.complete/11-gml-xsd-generation.md +42 -0
- data/TODO.complete/13-shapescript-interpreter.md +28 -0
- data/TODO.complete/20-cli-diff.md +46 -0
- data/TODO.complete/21-cli-render.md +26 -0
- data/TODO.complete/22-cli-export.md +29 -0
- data/TODO.complete/23-cli-mdg.md +23 -0
- data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
- data/TODO.complete/31-public-send-cleanup.md +61 -0
- data/TODO.complete/32-spec-coverage.md +33 -0
- data/TODO.complete/33-blocked-parity-gaps.md +41 -0
- data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
- data/TODO.complete/37-cli-lint.md +32 -0
- data/TODO.complete/38-cli-query.md +26 -0
- data/TODO.complete/39-cli-info.md +23 -0
- data/TODO.complete/40-ocl-evaluator.md +31 -0
- data/TODO.complete/41-round-trip-spec.md +18 -0
- data/TODO.complete/42-html-diff-report.md +18 -0
- data/TODO.complete/43-json-schema-export.md +20 -0
- data/TODO.complete/44-remaining-qea-tables.md +40 -0
- data/TODO.complete/45-shapescript-extensions.md +25 -0
- data/TODO.complete/46-xsd-consolidation.md +20 -0
- data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
- data/TODO.complete/STATUS.md +109 -0
- data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
- data/TODO.diagrams/01-xmi-extension-elements-parsing.md +67 -0
- data/TODO.diagrams/02-diagram-element-rich-model.md +61 -0
- data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
- data/TODO.diagrams/03-ea-style-svg-emitter.md +70 -0
- data/TODO.diagrams/03-text-over-rendering.md +30 -0
- data/TODO.diagrams/04-connector-routing-from-soid-eoid.md +55 -0
- data/TODO.diagrams/04-rect-under-rendering.md +33 -0
- data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
- data/TODO.diagrams/05-marker-rendering-per-relationship-type.md +49 -0
- data/TODO.diagrams/06-connector-labels.md +49 -0
- data/TODO.diagrams/06-group-layering-parity.md +39 -0
- data/TODO.diagrams/07-canvas-size-and-viewbox.md +39 -0
- data/TODO.diagrams/07-text-content-fidelity.md +65 -0
- data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
- data/TODO.diagrams/08-visual-regression-harness.md +34 -0
- data/TODO.diagrams/09-cli-render-all-diagrams.md +25 -0
- data/TODO.diagrams/10-stereotype-color-config.md +29 -0
- data/TODO.diagrams/11-elkrb-auto-layout-integration.md +53 -0
- data/TODO.diagrams/12-connector-labels.md +40 -0
- data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
- data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
- data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
- data/TODO.diagrams/14-center-header-text.md +33 -0
- data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
- data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
- data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
- data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
- data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
- data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
- data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
- data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
- data/TODO.diagrams/18-property-lookup-index.md +42 -0
- data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
- data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
- data/TODO.diagrams/20-split-labels-emitter.md +56 -0
- data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
- data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
- data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
- data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
- data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
- data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
- data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
- data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
- data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
- data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
- data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
- data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
- data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
- data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
- data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
- data/TODO.diagrams/36-instance-specification-model.md +42 -0
- data/TODO.diagrams/37-element-icon-decorations.md +41 -0
- data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
- data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
- data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
- data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
- data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
- data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
- data/TODO.diagrams/44-element-icon-decorations.md +34 -0
- data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
- data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
- data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
- data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
- data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
- data/TODO.diagrams/50-examples-parity.md +85 -0
- data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
- data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
- data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
- data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
- data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
- data/TODO.diagrams/56-package-contents-rendering.md +148 -0
- data/TODO.diagrams/57-instance-specification-model.md +100 -0
- data/TODO.diagrams/58-domain-model-note-text.md +67 -0
- data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
- data/TODO.diagrams/60-testschema-association-marker.md +40 -0
- data/TODO.diagrams/61-legend-block-rendering.md +55 -0
- data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
- data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
- data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
- data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
- data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/68-inherited-attributes.md +65 -0
- data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
- data/TODO.diagrams/70-mdg-technology-support.md +68 -0
- data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
- data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
- data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
- data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
- data/TODO.diagrams/75-plateau-text-gap.md +39 -0
- data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
- data/TODO.diagrams/77-basic-text-overrender.md +40 -0
- data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
- data/TODO.diagrams/79-architecture-backlog.md +65 -0
- data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
- data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
- data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
- data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
- data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
- data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
- data/TODO.diagrams/86-foreign-package-rule.md +62 -0
- data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
- data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
- data/TODO.diagrams/README.md +69 -0
- data/TODO.diagrams/STATUS.md +73 -0
- data/TODO.fixup/01-default-grouped-paths.md +34 -0
- data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
- data/TODO.fixup/03-diagram-font-inference.md +28 -0
- data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
- data/TODO.fixup/05-marker-registry-ocp.md +32 -0
- data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
- data/TODO.fixup/07-edge-attachment-formula.md +22 -0
- data/TODO.fixup/08-visual-diff-harness.md +28 -0
- data/TODO.fixup/09-extract-element-compartments.md +34 -0
- data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
- data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
- data/TODO.fixup/12-path-style-harmony.md +23 -0
- data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
- data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
- data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
- data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
- data/TODO.fixup/17-path-count-calibration.md +27 -0
- data/TODO.fixup/18-polygon-count-calibration.md +32 -0
- data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
- data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
- data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
- data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
- data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
- data/TODO.fixup/24-note-element-rendering.md +23 -0
- data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
- data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
- data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
- data/TODO.fixup/28-geometry-parser-identification.md +57 -0
- data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
- data/TODO.fixup/30-theme-system-stub.md +46 -0
- data/TODO.fixup/31-theme-value-object-registry.md +17 -0
- data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
- data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
- data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
- data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
- data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
- data/TODO.fixup/37-package-shape-rendering.md +22 -0
- data/TODO.fixup/38-note-element-rendering.md +15 -0
- data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
- data/TODO.fixup/40-connector-dashed-lines.md +15 -0
- data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
- data/TODO.fixup/42-textrenderer-centralization.md +10 -0
- data/TODO.fixup/43-colorresolver-unification.md +24 -0
- data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
- data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
- data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
- data/TODO.fixup/47-canvas-frame-inset.md +19 -0
- data/TODO.fixup/48-background-rect-format.md +15 -0
- data/TODO.fixup/49-all-samples-integration-test.md +17 -0
- data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
- data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
- data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
- data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
- data/TODO.fixup/54-diagram-theme-api.md +50 -0
- data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
- data/TODO.fixup/56-theme-yaml-files.md +65 -0
- data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
- data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
- data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
- data/TODO.fixup/60-cli-theme-option.md +22 -0
- data/TODO.fixup/61-theme-validation.md +28 -0
- data/TODO.fixup/62-theme-builder-dsl.md +31 -0
- data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
- data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
- data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
- data/TODO.fixup/66-styleex-writeback.md +22 -0
- data/TODO.fixup/67-delete-dead-code.md +28 -0
- data/TODO.fixup/68-figure-parity-status.md +82 -0
- data/TODO.fixup/69-package-polygon-rendering.md +54 -0
- data/TODO.fixup/70-tagged-values-compartment.md +39 -0
- data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
- data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
- data/TODO.fixup/73-delete-dead-transformations.md +14 -0
- data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
- data/TODO.fixup/75-visibility-legend-icons.md +23 -0
- data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
- data/TODO.fixup/77-canvas-frame-margins.md +28 -0
- data/TODO.fixup/78-note-element-rendering.md +30 -0
- data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
- data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
- data/TODO.fixup/81-figure-parity-current-state.md +63 -0
- data/config/qea_schema.yml +356 -0
- data/config/stereotype_colors.yml +15 -0
- data/config/themes/119.yml +98 -0
- data/config/themes/default.yml +14 -0
- data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
- data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
- data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
- data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
- data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
- data/examples/exports/UmlModel_template/model.xml +23 -0
- data/examples/exports/basic/UML_EA.DTD +6856 -0
- data/examples/exports/basic/model.xml +8039 -0
- data/examples/exports/simple/UML_EA.DTD +6856 -0
- data/examples/exports/simple/model.xml +585 -0
- data/examples/exports/simple_example/UML_EA.DTD +6856 -0
- data/examples/exports/simple_example/model.xml +23 -0
- data/examples/exports/test/UML_EA.DTD +6856 -0
- data/examples/exports/test/model.xml +1638 -0
- data/examples/qea/basic.qea +0 -0
- data/frontend/README.md +51 -0
- data/frontend/dist/app.iife.js +24 -0
- data/frontend/dist/style.css +1 -0
- data/frontend/index.html +26 -0
- data/frontend/package-lock.json +2359 -0
- data/frontend/package.json +26 -0
- data/frontend/src/App.vue +59 -0
- data/frontend/src/app.ts +8 -0
- data/frontend/src/components/AppHeader.vue +127 -0
- data/frontend/src/components/AppSidebar.vue +119 -0
- data/frontend/src/components/ClassDetails.vue +247 -0
- data/frontend/src/components/DiagramView.vue +79 -0
- data/frontend/src/components/PackageDetails.vue +92 -0
- data/frontend/src/components/PackageTreeNode.vue +64 -0
- data/frontend/src/components/SearchResults.vue +48 -0
- data/frontend/src/components/WelcomeView.vue +87 -0
- data/frontend/src/shims-vue.d.ts +5 -0
- data/frontend/src/stores/dataStore.ts +79 -0
- data/frontend/src/stores/uiStore.ts +167 -0
- data/frontend/src/styles/main.css +1566 -0
- data/frontend/src/types.ts +221 -0
- data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
- data/frontend/tests/e2e/render.spec.ts +188 -0
- data/frontend/tsconfig.json +20 -0
- data/frontend/vite.config.ts +26 -0
- data/frontend/vitest.config.ts +8 -0
- data/lib/ea/cli/app.rb +62 -1
- data/lib/ea/cli/command/diagrams.rb +37 -28
- data/lib/ea/cli/command/diff.rb +46 -0
- data/lib/ea/cli/command/export.rb +80 -0
- data/lib/ea/cli/command/info.rb +75 -0
- data/lib/ea/cli/command/lint.rb +48 -0
- data/lib/ea/cli/command/mdg.rb +98 -0
- data/lib/ea/cli/command/query.rb +34 -0
- data/lib/ea/cli/command/render.rb +135 -0
- data/lib/ea/cli/command/svg.rb +43 -6
- data/lib/ea/cli/command.rb +7 -0
- data/lib/ea/diagram/display_config.rb +184 -0
- data/lib/ea/diagram.rb +4 -41
- data/lib/ea/diff/change.rb +30 -0
- data/lib/ea/diff/comparator.rb +82 -0
- data/lib/ea/diff/html_reporter.rb +91 -0
- data/lib/ea/diff.rb +15 -0
- data/lib/ea/export/json/generator.rb +42 -0
- data/lib/ea/export/json.rb +12 -0
- data/lib/ea/export/json_schema/generator.rb +77 -0
- data/lib/ea/export/json_schema.rb +12 -0
- data/lib/ea/export/plantuml/generator.rb +73 -0
- data/lib/ea/export/plantuml.rb +12 -0
- data/lib/ea/export/xmi/generator.rb +77 -0
- data/lib/ea/export/xmi.rb +11 -0
- data/lib/ea/export/xsd/class_mapping.rb +65 -0
- data/lib/ea/export/xsd/generator.rb +142 -0
- data/lib/ea/export/xsd/namespace_registry.rb +82 -0
- data/lib/ea/export/xsd.rb +19 -0
- data/lib/ea/export.rb +14 -0
- data/lib/ea/image/emf_renderer.rb +42 -0
- data/lib/ea/image.rb +12 -0
- data/lib/ea/lint/engine.rb +30 -0
- data/lib/ea/lint/lint_rule.rb +38 -0
- data/lib/ea/lint/offense.rb +18 -0
- data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
- data/lib/ea/lint/rules/duplicate_name.rb +28 -0
- data/lib/ea/lint/rules/missing_stereotype.rb +35 -0
- data/lib/ea/lint/rules/naming_convention.rb +44 -0
- data/lib/ea/lint/rules/orphan_element.rb +28 -0
- data/lib/ea/lint/rules.rb +14 -0
- data/lib/ea/lint.rb +13 -0
- data/lib/ea/mdg/document.rb +120 -0
- data/lib/ea/mdg/loader.rb +118 -0
- data/lib/ea/mdg/registry.rb +98 -0
- data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
- data/lib/ea/mdg/xml/applies_to.rb +18 -0
- data/lib/ea/mdg/xml/apply.rb +18 -0
- data/lib/ea/mdg/xml/content.rb +18 -0
- data/lib/ea/mdg/xml/documentation.rb +24 -0
- data/lib/ea/mdg/xml/stereotype.rb +28 -0
- data/lib/ea/mdg/xml/stereotypes.rb +18 -0
- data/lib/ea/mdg/xml/tag.rb +28 -0
- data/lib/ea/mdg/xml/tagged_values.rb +18 -0
- data/lib/ea/mdg/xml/technology.rb +28 -0
- data/lib/ea/mdg/xml/uml_profile.rb +22 -0
- data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
- data/lib/ea/mdg/xml.rb +24 -0
- data/lib/ea/mdg.rb +25 -0
- data/lib/ea/model/classifier.rb +4 -0
- data/lib/ea/model/constraint.rb +24 -0
- data/lib/ea/model/diagram.rb +85 -0
- data/lib/ea/model/diagram_connector.rb +53 -1
- data/lib/ea/model/diagram_element.rb +44 -0
- data/lib/ea/model/document.rb +47 -2
- data/lib/ea/model/ghost_label.rb +32 -0
- data/lib/ea/model/instance_specification.rb +34 -0
- data/lib/ea/model/legend.rb +43 -0
- data/lib/ea/model/legend_item.rb +26 -0
- data/lib/ea/model/note.rb +28 -0
- data/lib/ea/model/operation.rb +2 -0
- data/lib/ea/model/property.rb +2 -0
- data/lib/ea/model/relationship.rb +2 -0
- data/lib/ea/model/signal.rb +14 -0
- data/lib/ea/model/slot.rb +19 -0
- data/lib/ea/model.rb +8 -0
- data/lib/ea/ocl/evaluator.rb +94 -0
- data/lib/ea/ocl/nodes.rb +30 -0
- data/lib/ea/ocl/parser.rb +133 -0
- data/lib/ea/ocl/unsupported_error.rb +7 -0
- data/lib/ea/ocl.rb +13 -0
- data/lib/ea/qea/factory/document_builder.rb +24 -10
- data/lib/ea/qea/factory/generalization_builder.rb +9 -11
- data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
- data/lib/ea/qea/models/ea_author.rb +35 -0
- data/lib/ea/qea/models/ea_glossary.rb +34 -0
- data/lib/ea/qea/models/ea_image.rb +53 -0
- data/lib/ea/qea/models/ea_implement.rb +32 -0
- data/lib/ea/qea/models/ea_list.rb +42 -0
- data/lib/ea/qea/models/ea_object_effort.rb +34 -0
- data/lib/ea/qea/models/ea_object_problem.rb +37 -0
- data/lib/ea/qea/models/ea_object_require.rb +34 -0
- data/lib/ea/qea/models/ea_object_resource.rb +34 -0
- data/lib/ea/qea/models/ea_object_risk.rb +34 -0
- data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
- data/lib/ea/qea/models/ea_object_test.rb +35 -0
- data/lib/ea/qea/models/ea_object_trx.rb +34 -0
- data/lib/ea/qea/models/ea_operation.rb +7 -0
- data/lib/ea/qea/models/ea_palette.rb +30 -0
- data/lib/ea/qea/models/ea_palette_item.rb +36 -0
- data/lib/ea/qea/models/ea_phase.rb +40 -0
- data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
- data/lib/ea/qea/models/ea_secrypt.rb +34 -0
- data/lib/ea/qea/models/ea_version.rb +39 -0
- data/lib/ea/qea/models/ea_xref.rb +11 -1
- data/lib/ea/qea/models.rb +19 -0
- data/lib/ea/qea/services/database_loader.rb +22 -1
- data/lib/ea/query/builder.rb +108 -0
- data/lib/ea/query.rb +9 -0
- data/lib/ea/render/image_converter.rb +101 -0
- data/lib/ea/render.rb +10 -0
- data/lib/ea/shapescript/parser.rb +208 -0
- data/lib/ea/shapescript/renderer.rb +74 -0
- data/lib/ea/shapescript/shape.rb +34 -0
- data/lib/ea/shapescript.rb +24 -0
- data/lib/ea/sources/qea/adapter.rb +22 -5
- data/lib/ea/sources/qea/classifier_builder.rb +184 -7
- data/lib/ea/sources/qea/diagram_builder.rb +352 -24
- data/lib/ea/sources/qea/id_normalizer.rb +16 -1
- data/lib/ea/sources/qea/instance_builder.rb +113 -0
- data/lib/ea/sources/qea/legend_builder.rb +137 -0
- data/lib/ea/sources/qea/note_builder.rb +58 -0
- data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
- data/lib/ea/sources/qea/operation_builder.rb +1 -0
- data/lib/ea/sources/qea/package_builder.rb +16 -0
- data/lib/ea/sources/qea/relationship_builder.rb +21 -5
- data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
- data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
- data/lib/ea/sources/qea/xref/parser.rb +150 -0
- data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
- data/lib/ea/sources/qea/xref/record.rb +41 -0
- data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
- data/lib/ea/sources/qea/xref.rb +27 -0
- data/lib/ea/sources/qea.rb +4 -0
- data/lib/ea/sources/xmi/adapter.rb +80 -2
- data/lib/ea/sources/xmi/annotation_builder.rb +1 -1
- data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
- data/lib/ea/sources/xmi/diagram_builder.rb +242 -45
- data/lib/ea/sources/xmi/extension_elements.rb +74 -0
- data/lib/ea/sources/xmi/extension_geometry_parser.rb +124 -0
- data/lib/ea/sources/xmi/extension_style_parser.rb +91 -0
- data/lib/ea/sources/xmi/note_builder.rb +46 -0
- data/lib/ea/sources/xmi/property_builder.rb +9 -0
- data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
- data/lib/ea/sources/xmi/tag_builder.rb +45 -0
- data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
- data/lib/ea/sources/xmi.rb +7 -0
- data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
- data/lib/ea/spa/output/single_file_strategy.rb +8 -2
- data/lib/ea/spa/output/strategy.rb +34 -0
- data/lib/ea/svg/connector_router.rb +134 -0
- data/lib/ea/svg/ea_emitter/background.rb +15 -0
- data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
- data/lib/ea/svg/ea_emitter/canvas.rb +88 -0
- data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
- data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
- data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
- data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
- data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
- data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
- data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
- data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
- data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
- data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
- data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
- data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment.rb +58 -0
- data/lib/ea/svg/ea_emitter/connectors.rb +84 -0
- data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
- data/lib/ea/svg/ea_emitter/document.rb +35 -0
- data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
- data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
- data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
- data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
- data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
- data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
- data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
- data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
- data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
- data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
- data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
- data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
- data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
- data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
- data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
- data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
- data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +82 -0
- data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
- data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
- data/lib/ea/svg/ea_emitter/element.rb +37 -0
- data/lib/ea/svg/ea_emitter/elements.rb +357 -0
- data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
- data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
- data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
- data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
- data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
- data/lib/ea/svg/ea_emitter/label.rb +23 -0
- data/lib/ea/svg/ea_emitter/labels.rb +130 -0
- data/lib/ea/svg/ea_emitter/layer.rb +20 -0
- data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
- data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
- data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
- data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
- data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
- data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
- data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
- data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
- data/lib/ea/svg/ea_emitter/marker.rb +37 -0
- data/lib/ea/svg/ea_emitter/markers.rb +404 -0
- data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
- data/lib/ea/svg/ea_emitter/style.rb +15 -0
- data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
- data/lib/ea/svg/ea_emitter.rb +51 -0
- data/lib/ea/svg/parity/checker.rb +105 -0
- data/lib/ea/svg/parity/source.rb +59 -0
- data/lib/ea/svg/parity/suite.rb +113 -0
- data/lib/ea/svg/parity.rb +14 -0
- data/lib/ea/svg/stereotype_color_resolver.rb +50 -0
- data/lib/ea/svg.rb +4 -15
- data/lib/ea/theme/definition.rb +176 -0
- data/lib/ea/theme/loader.rb +125 -0
- data/lib/ea/theme/registry.rb +96 -0
- data/lib/ea/theme.rb +37 -0
- data/lib/ea/transformations.rb +5 -88
- data/lib/ea/version.rb +4 -1
- data/lib/ea.rb +14 -4
- data/lib/tasks/svg_bench.rake +87 -0
- data/lutaml-uml-0.5.1.gem +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
- metadata +473 -26
- data/lib/ea/diagram/configuration.rb +0 -379
- data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
- data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
- data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
- data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
- data/lib/ea/diagram/element_renderers.rb +0 -43
- data/lib/ea/diagram/extractor.rb +0 -566
- data/lib/ea/diagram/layout_engine.rb +0 -170
- data/lib/ea/diagram/path_builder.rb +0 -202
- data/lib/ea/diagram/style_parser.rb +0 -42
- data/lib/ea/diagram/style_resolver.rb +0 -291
- data/lib/ea/diagram/svg_renderer.rb +0 -274
- data/lib/ea/diagram/util.rb +0 -73
- data/lib/ea/svg/bounds_calculator.rb +0 -74
- data/lib/ea/svg/connector_path.rb +0 -138
- data/lib/ea/svg/element_box.rb +0 -256
- data/lib/ea/svg/renderer.rb +0 -77
- data/lib/ea/svg/style_resolver.rb +0 -72
- data/lib/ea/transformations/configuration.rb +0 -333
- data/lib/ea/transformations/format_registry.rb +0 -366
- data/lib/ea/transformations/parsers/base_parser.rb +0 -482
- data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
- data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
- data/lib/ea/transformations/transformation_engine.rb +0 -390
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
# Emits the elements layer. Orchestrates per-element rendering
|
|
7
|
+
# by delegating to specialized compartment renderers (shape,
|
|
8
|
+
# header, divider, attribute). Frame-element filtering and
|
|
9
|
+
# stereotype-color fallback also live in dedicated
|
|
10
|
+
# collaborators.
|
|
11
|
+
class Elements
|
|
12
|
+
DEFAULT_FILL = "#FFFFFF"
|
|
13
|
+
DEFAULT_STROKE = "#000000"
|
|
14
|
+
DEFAULT_STROKE_WIDTH = 2
|
|
15
|
+
DEFAULT_TEXT_COLOR = "#000000"
|
|
16
|
+
|
|
17
|
+
attr_reader :diagram, :model_index, :canvas, :document
|
|
18
|
+
|
|
19
|
+
def initialize(diagram, model_index:, canvas: nil, document: nil)
|
|
20
|
+
@diagram = diagram
|
|
21
|
+
@model_index = model_index
|
|
22
|
+
@canvas = canvas
|
|
23
|
+
@document = document
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def render
|
|
27
|
+
groups.join("\n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns an Array of per-entity `<g>...</g>` strings.
|
|
31
|
+
# Each element contributes up to 4 groups (shape, header,
|
|
32
|
+
# divider, attrs) in EA's per-entity layer order.
|
|
33
|
+
def groups
|
|
34
|
+
ordered_elements.flat_map { |e| groups_for(e) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def ordered_elements
|
|
40
|
+
(diagram.elements || []).sort_by { |e| e.z_order || 0 }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def groups_for(element)
|
|
44
|
+
return [] if element_filter.skip?(element)
|
|
45
|
+
|
|
46
|
+
context = build_context(element)
|
|
47
|
+
return [] unless context
|
|
48
|
+
|
|
49
|
+
Compartment.render_all(context).compact
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Build the RenderContext for one element. Returns nil when
|
|
53
|
+
# the element has no usable bounds (skip rendering).
|
|
54
|
+
def build_context(element)
|
|
55
|
+
raw_bounds = element.bounds || element.image_bounds
|
|
56
|
+
return nil unless raw_bounds
|
|
57
|
+
|
|
58
|
+
bounds = translate_bounds(raw_bounds)
|
|
59
|
+
model_element = model_element_for(element)
|
|
60
|
+
classifier = classifier_for(element)
|
|
61
|
+
size = font_resolver.size_for(element)
|
|
62
|
+
family = font_resolver.family_for(element)
|
|
63
|
+
size_unit = font_resolver.size_unit_for(element)
|
|
64
|
+
parent_name = off_canvas_parent_name_for(classifier)
|
|
65
|
+
|
|
66
|
+
header_lines = classifier ? Element::HeaderLines.for(classifier,
|
|
67
|
+
diagram_package_id: diagram.package_id,
|
|
68
|
+
visually_nested: visually_nested?(element),
|
|
69
|
+
umldi_keyword: element.umldi_keyword,
|
|
70
|
+
bounds_width: raw_bounds&.width,
|
|
71
|
+
font_size: size,
|
|
72
|
+
off_canvas_parent_name: parent_name) : []
|
|
73
|
+
is_classifier = classifier.is_a?(Ea::Model::Classifier)
|
|
74
|
+
attr_lines = (is_classifier && show_attributes?) ? Element::AttributeRenderer.lines_for(classifier, lookup: attribute_lookup) : []
|
|
75
|
+
op_lines = (is_classifier && show_operations?) ? Element::OperationRenderer.lines_for(classifier) : []
|
|
76
|
+
geometry = CompartmentGeometry.new(bounds: bounds, size: size,
|
|
77
|
+
header_lines_count: header_lines.size,
|
|
78
|
+
attr_lines_count: attr_lines.size,
|
|
79
|
+
op_lines_count: op_lines.size,
|
|
80
|
+
tagged_values_count: tagged_values_for(classifier).size,
|
|
81
|
+
header_top_padding: theme.compartments.header_top_padding,
|
|
82
|
+
header_line_offset: theme.compartments.header_line_offset,
|
|
83
|
+
divider_offset: theme.compartments.divider_offset,
|
|
84
|
+
attr_line_offset: theme.compartments.attr_line_offset,
|
|
85
|
+
attr_first_offset: theme.compartments.attr_first_offset)
|
|
86
|
+
RenderContext.new(
|
|
87
|
+
element: element,
|
|
88
|
+
bounds: bounds,
|
|
89
|
+
model_element: model_element,
|
|
90
|
+
classifier: classifier,
|
|
91
|
+
fill: resolve_fill(element, classifier),
|
|
92
|
+
stroke: resolve_stroke,
|
|
93
|
+
stroke_width: resolve_stroke_width,
|
|
94
|
+
text_fill: theme.text_color,
|
|
95
|
+
family: family, size: size, size_unit: size_unit,
|
|
96
|
+
header_lines: header_lines,
|
|
97
|
+
attr_lines: attr_lines,
|
|
98
|
+
op_lines: op_lines,
|
|
99
|
+
enum_literals: enum_literals_for(classifier),
|
|
100
|
+
tagged_values: element.show_tagged_values ? tagged_values_for(classifier) : [],
|
|
101
|
+
constraints: constraints_for(classifier),
|
|
102
|
+
package_content_lines: package_content_lines_for(model_element),
|
|
103
|
+
geometry: geometry,
|
|
104
|
+
theme: theme,
|
|
105
|
+
canvas: canvas,
|
|
106
|
+
diagram: diagram,
|
|
107
|
+
model_index: model_index,
|
|
108
|
+
off_canvas_parent_name: parent_name
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def enum_literals_for(classifier)
|
|
113
|
+
return [] unless classifier.is_a?(Ea::Model::Enumeration)
|
|
114
|
+
|
|
115
|
+
classifier.literals || []
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def tagged_values_for(classifier)
|
|
119
|
+
return [] unless classifier.is_a?(Ea::Model::Classifier)
|
|
120
|
+
|
|
121
|
+
classifier.tagged_values || []
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def constraints_for(classifier)
|
|
125
|
+
return [] unless classifier.is_a?(Ea::Model::Classifier)
|
|
126
|
+
|
|
127
|
+
classifier.constraints || []
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# EA renders the off-canvas parent classifier's name as an
|
|
131
|
+
# italic line at the top of a placed element's header when
|
|
132
|
+
# ALL of these hold:
|
|
133
|
+
#
|
|
134
|
+
# 1. The diagram's HideParents flag is 0 (the default).
|
|
135
|
+
# HideParents=1 suppresses the ghost line entirely.
|
|
136
|
+
# 2. The parent (general) of a Generalization is NOT placed
|
|
137
|
+
# on the current diagram.
|
|
138
|
+
# 3. No other element from the parent's package is placed
|
|
139
|
+
# on the diagram — EA treats the parent's package as
|
|
140
|
+
# "represented" and omits the ghost as redundant.
|
|
141
|
+
def off_canvas_parent_name_for(classifier)
|
|
142
|
+
return nil unless classifier.is_a?(Ea::Model::Classifier)
|
|
143
|
+
return nil unless document
|
|
144
|
+
return nil unless diagram.show_parents
|
|
145
|
+
|
|
146
|
+
gen = parent_generalization_for(classifier)
|
|
147
|
+
return nil unless gen
|
|
148
|
+
|
|
149
|
+
parent = model_index[gen.general_id]
|
|
150
|
+
return nil unless parent
|
|
151
|
+
return nil if placed_on_diagram?(gen.general_id)
|
|
152
|
+
return nil if parent_package_represented_on_diagram?(parent)
|
|
153
|
+
|
|
154
|
+
name = parent.name.to_s
|
|
155
|
+
name.empty? ? nil : name
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def parent_generalization_for(classifier)
|
|
159
|
+
document.relationships.find do |rel|
|
|
160
|
+
rel.is_a?(Ea::Model::Generalization) &&
|
|
161
|
+
rel.specific_id == classifier.id
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def placed_on_diagram?(model_element_ref)
|
|
166
|
+
(diagram.elements || []).any? { |e| e.model_element_ref == model_element_ref }
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Returns true when any element placed on the current diagram
|
|
170
|
+
# lives in the same package as `parent`. EA suppresses the
|
|
171
|
+
# parent-class ghost line in that case — the parent's package
|
|
172
|
+
# is already "represented" on the diagram, so the off-canvas
|
|
173
|
+
# parent is implicitly visible to the reader.
|
|
174
|
+
def parent_package_represented_on_diagram?(parent)
|
|
175
|
+
parent_pkg = parent.is_a?(Ea::Model::Classifier) ? parent.package_id : nil
|
|
176
|
+
return false unless parent_pkg
|
|
177
|
+
|
|
178
|
+
(diagram.elements || []).any? do |e|
|
|
179
|
+
sibling = model_index[e.model_element_ref]
|
|
180
|
+
sibling.is_a?(Ea::Model::Classifier) && sibling.package_id == parent_pkg
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# EA renders a package's child classifiers and sub-packages as
|
|
185
|
+
# alphabetical "+ Name" rows in the body compartment, but
|
|
186
|
+
# only when the diagram's t_diagram.ShowPackageContents flag
|
|
187
|
+
# is non-zero. Returns [] when the diagram suppresses
|
|
188
|
+
# package contents or the model element isn't a Package.
|
|
189
|
+
# Each entry is a Struct(name:, kind:) so the renderer can
|
|
190
|
+
# pick the correct per-type icon (Enumeration vs default).
|
|
191
|
+
def package_content_lines_for(model_element)
|
|
192
|
+
return [] unless model_element.is_a?(Ea::Model::Package)
|
|
193
|
+
return [] unless diagram.show_package_contents
|
|
194
|
+
|
|
195
|
+
children = package_children(model_element)
|
|
196
|
+
return [] if children.empty?
|
|
197
|
+
|
|
198
|
+
children.sort_by { |c| c.name.to_s }.map do |c|
|
|
199
|
+
PackageContentRow.new(name: c.name.to_s, kind: row_icon_kind(c))
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def package_children(package)
|
|
204
|
+
return [] unless model_index
|
|
205
|
+
|
|
206
|
+
model_index.values.select do |obj|
|
|
207
|
+
(obj.is_a?(Ea::Model::Classifier) && obj.package_id == package.id) ||
|
|
208
|
+
(obj.is_a?(Ea::Model::Package) && obj.parent_id == package.id)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Discriminator for the per-row icon shape. EA draws a
|
|
213
|
+
# list-style icon for Enumeration children (or classifiers
|
|
214
|
+
# stereotyped "enumeration"), a small 13×9 folder icon for
|
|
215
|
+
# sub-Packages, and a "folded paper" icon for other
|
|
216
|
+
# classifiers.
|
|
217
|
+
def row_icon_kind(child)
|
|
218
|
+
if child.is_a?(Ea::Model::Package)
|
|
219
|
+
:package
|
|
220
|
+
elsif child.is_a?(Ea::Model::Enumeration) ||
|
|
221
|
+
enumeration_stereotype?(child)
|
|
222
|
+
:enumeration
|
|
223
|
+
else
|
|
224
|
+
:default
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def enumeration_stereotype?(child)
|
|
229
|
+
return false unless child.is_a?(Ea::Model::Classifier)
|
|
230
|
+
|
|
231
|
+
refs = child.stereotype_refs
|
|
232
|
+
refs&.any? { |r| r.to_s.downcase == "enumeration" }
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
PackageContentRow = Struct.new(:name, :kind, keyword_init: true)
|
|
236
|
+
|
|
237
|
+
# Computes y-coordinates for header/divider/attr compartments
|
|
238
|
+
# given bounds, font size, and line count. See
|
|
239
|
+
# Element::CompartmentGeometry for the coordinate math.
|
|
240
|
+
CompartmentGeometry = Element::CompartmentGeometry
|
|
241
|
+
|
|
242
|
+
# Returns true when this element's bounds are geometrically
|
|
243
|
+
# inside another element's bounds in the same diagram. EA
|
|
244
|
+
# shows qualified names (ParentClass::ChildClass) for
|
|
245
|
+
# visually-nested classes and simple names for separately-
|
|
246
|
+
# placed classes.
|
|
247
|
+
def visually_nested?(element)
|
|
248
|
+
my_bounds = element.bounds || element.image_bounds
|
|
249
|
+
return false unless my_bounds
|
|
250
|
+
|
|
251
|
+
(diagram.elements || []).any? do |other|
|
|
252
|
+
next if other.id == element.id
|
|
253
|
+
|
|
254
|
+
other_bounds = other.bounds || other.image_bounds
|
|
255
|
+
next unless other_bounds
|
|
256
|
+
|
|
257
|
+
bounds_contain?(other_bounds, my_bounds)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def bounds_contain?(outer, inner)
|
|
262
|
+
outer.x <= inner.x &&
|
|
263
|
+
outer.y <= inner.y &&
|
|
264
|
+
outer.x + outer.width >= inner.x + inner.width &&
|
|
265
|
+
outer.y + outer.height >= inner.y + inner.height
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def resolve_fill(element, classifier)
|
|
269
|
+
color_resolver.fill_for(element, classifier)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def resolve_stroke
|
|
273
|
+
color_resolver.stroke_for(nil)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def resolve_stroke_width
|
|
277
|
+
theme.themed? ? theme.stroke_width : DEFAULT_STROKE_WIDTH
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def color_resolver
|
|
281
|
+
@color_resolver ||= ColorResolver.new(theme: theme)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def theme
|
|
285
|
+
@theme ||= diagram.theme
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def display_config
|
|
289
|
+
@display_config ||= diagram.display_config
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def show_attributes?
|
|
293
|
+
display_config.show_attributes?
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def show_operations?
|
|
297
|
+
display_config.show_operations?
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# EA only renders a header→content divider when there's
|
|
301
|
+
# actual content below (attributes, operations, literals, or
|
|
302
|
+
# tagged values). Classes with only a header (no displayed
|
|
303
|
+
# features) omit the divider entirely.
|
|
304
|
+
def has_content_below_header?(attr_lines, op_lines, enum_literals, tagged_values = [])
|
|
305
|
+
!attr_lines.empty? || !op_lines.empty? || enum_literals.any? || tagged_values.any?
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def font_resolver
|
|
309
|
+
@font_resolver ||= FontResolver.new(diagram, theme: theme)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def element_filter
|
|
313
|
+
@element_filter ||= Element::Filter.new(model_index: model_index)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def classifier_for(element)
|
|
317
|
+
ref = element.model_element_ref
|
|
318
|
+
return nil unless ref
|
|
319
|
+
|
|
320
|
+
candidate = model_index[ref]
|
|
321
|
+
return nil unless candidate.is_a?(Ea::Model::Classifier) ||
|
|
322
|
+
candidate.is_a?(Ea::Model::InstanceSpecification)
|
|
323
|
+
|
|
324
|
+
candidate
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Lookup proc for AttributeLineBuilder: resolves a
|
|
328
|
+
# classifier id to the Classifier instance. Returns nil
|
|
329
|
+
# when not found or not a Classifier.
|
|
330
|
+
def attribute_lookup
|
|
331
|
+
lambda { |id| model_index[id] if model_index && model_index[id].is_a?(Ea::Model::Classifier) }
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def model_element_for(element)
|
|
335
|
+
ref = element.model_element_ref
|
|
336
|
+
return nil unless ref
|
|
337
|
+
|
|
338
|
+
model_index[ref]
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def translate_bounds(b)
|
|
342
|
+
return b unless canvas
|
|
343
|
+
|
|
344
|
+
# Reuse the model's Bounds value object rather than an
|
|
345
|
+
# ad-hoc OpenStruct. Keeps the type uniform across the
|
|
346
|
+
# pipeline and avoids require "ostruct".
|
|
347
|
+
Ea::Model::Bounds.new(
|
|
348
|
+
x: canvas.translate_x(b.x),
|
|
349
|
+
y: canvas.translate_y(b.y),
|
|
350
|
+
width: b.width,
|
|
351
|
+
height: b.height
|
|
352
|
+
)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
# Resolves the effective font family + size for a DiagramElement,
|
|
7
|
+
# using EA's fallback chain:
|
|
8
|
+
#
|
|
9
|
+
# 1. element.font_family / element.font_size (per-element override)
|
|
10
|
+
# 2. theme.font_family / theme.font_size (when theme is themed)
|
|
11
|
+
# 3. diagram-level default (most common non-nil value across
|
|
12
|
+
# the diagram's elements)
|
|
13
|
+
# 4. locale fallback (Calibri 10)
|
|
14
|
+
#
|
|
15
|
+
# Lives outside the Elements emitter so the resolution rule is
|
|
16
|
+
# defined once (MECE) and reusable by Labels and any future
|
|
17
|
+
# text-emitting layer.
|
|
18
|
+
class FontResolver
|
|
19
|
+
DEFAULT_FONT_FAMILY = "Yu Gothic UI"
|
|
20
|
+
# EA's default element compartment font size (matches
|
|
21
|
+
# reference SVGs which use 9pt for classifier header,
|
|
22
|
+
# attributes, operations, etc. — distinct from theme's
|
|
23
|
+
# diagram-level font size which applies only to frame
|
|
24
|
+
# label and swimlanes).
|
|
25
|
+
DEFAULT_ELEMENT_FONT_SIZE = 9
|
|
26
|
+
|
|
27
|
+
attr_reader :diagram, :theme
|
|
28
|
+
|
|
29
|
+
def initialize(diagram, theme: nil)
|
|
30
|
+
@diagram = diagram
|
|
31
|
+
@theme = theme || Ea::Theme::Registry.default
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def family_for(element)
|
|
35
|
+
explicit = element&.font_family
|
|
36
|
+
return explicit if explicit && !explicit.empty?
|
|
37
|
+
return theme.font_family if theme.themed? && theme.font_family
|
|
38
|
+
|
|
39
|
+
diagram_default_family || DEFAULT_FONT_FAMILY
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def size_for(element)
|
|
43
|
+
explicit = element&.font_size
|
|
44
|
+
explicit = nil if explicit&.zero?
|
|
45
|
+
return explicit if explicit
|
|
46
|
+
return diagram_default_size if diagram_default_size
|
|
47
|
+
|
|
48
|
+
DEFAULT_ELEMENT_FONT_SIZE
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def size_unit_for(_element)
|
|
52
|
+
theme.font_size_unit || "pt"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def weight_for(element)
|
|
56
|
+
bold = element&.font_bold ? 700 : nil
|
|
57
|
+
bold || (theme.themed? ? theme.text_weight_normal : 400)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def style_for(element)
|
|
61
|
+
element&.font_italic ? "italic" : "normal"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# EA's app-default font depends on the system locale:
|
|
67
|
+
# Japanese Windows → "Yu Gothic UI" 13, English Windows →
|
|
68
|
+
# "Calibri" 10. We can't query EA's runtime, so we infer
|
|
69
|
+
# from the diagram:
|
|
70
|
+
#
|
|
71
|
+
# - If ANY element specifies font=, use the most common.
|
|
72
|
+
# - If NO element specifies font= (all are "use default"),
|
|
73
|
+
# assume the EA English-locale default: Calibri 10.
|
|
74
|
+
def diagram_default_family
|
|
75
|
+
families = element_families
|
|
76
|
+
return FALLBACK_DEFAULT_FAMILY if families.empty?
|
|
77
|
+
|
|
78
|
+
most_common(families)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def diagram_default_size
|
|
82
|
+
sizes = element_sizes
|
|
83
|
+
return nil if sizes.empty?
|
|
84
|
+
|
|
85
|
+
most_common(sizes)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
FALLBACK_DEFAULT_FAMILY = "Calibri"
|
|
89
|
+
|
|
90
|
+
def most_common(values)
|
|
91
|
+
return nil if values.empty?
|
|
92
|
+
|
|
93
|
+
tally = tally_values(values)
|
|
94
|
+
tally.max_by { |_, count| count }.first
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def element_families
|
|
98
|
+
(diagram.elements || []).map(&:font_family).compact
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def element_sizes
|
|
102
|
+
(diagram.elements || []).map(&:font_size).compact.reject(&:zero?)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def tally_values(values)
|
|
106
|
+
values.each_with_object(Hash.new(0)) { |v, acc| acc[v] += 1 }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
# Emits ghost classifier name `<text>` elements for connector
|
|
7
|
+
# endpoints that reference off-canvas classifiers. Each ghost
|
|
8
|
+
# label appears as italic text near the connector's on-canvas
|
|
9
|
+
# endpoint.
|
|
10
|
+
class GhostLabels
|
|
11
|
+
DEFAULT_FAMILY = "Carlito"
|
|
12
|
+
DEFAULT_SIZE = 9
|
|
13
|
+
DEFAULT_UNIT = "pt"
|
|
14
|
+
DEFAULT_FILL = "#000000"
|
|
15
|
+
|
|
16
|
+
attr_reader :diagram, :canvas, :family, :size, :size_unit, :fill
|
|
17
|
+
|
|
18
|
+
def initialize(diagram, canvas: nil, family: DEFAULT_FAMILY,
|
|
19
|
+
size: DEFAULT_SIZE, size_unit: DEFAULT_UNIT,
|
|
20
|
+
fill: DEFAULT_FILL)
|
|
21
|
+
@diagram = diagram
|
|
22
|
+
@canvas = canvas
|
|
23
|
+
@family = family
|
|
24
|
+
@size = size
|
|
25
|
+
@size_unit = size_unit
|
|
26
|
+
@fill = fill
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render
|
|
30
|
+
texts = visible_connectors.flat_map do |connector|
|
|
31
|
+
(connector.ghost_labels || []).map { |g| text_for(g) }
|
|
32
|
+
end
|
|
33
|
+
return "" if texts.empty?
|
|
34
|
+
|
|
35
|
+
group_style = "stroke-width:1;stroke-linecap:round;" \
|
|
36
|
+
"stroke-linejoin:bevel; fill:#{fill};" \
|
|
37
|
+
"fill-opacity:1.00; stroke:#000000;" \
|
|
38
|
+
" stroke-opacity:0.00"
|
|
39
|
+
%(<g style="#{group_style}">\n#{texts.join("\n")}\n</g>)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def visible_connectors
|
|
45
|
+
(diagram.connectors || []).select(&:renderable?)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def text_for(ghost)
|
|
49
|
+
tx, ty = translate(ghost.anchor_x, ghost.anchor_y)
|
|
50
|
+
TextRenderer.new(
|
|
51
|
+
content: ghost.name.to_s,
|
|
52
|
+
x: tx, y: ty,
|
|
53
|
+
family: family, size: size, size_unit: size_unit,
|
|
54
|
+
style: "italic", fill: fill
|
|
55
|
+
).to_svg
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def translate(x, y)
|
|
59
|
+
return [x, y] unless canvas
|
|
60
|
+
|
|
61
|
+
[canvas.translate_x(x), canvas.translate_y(y)]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|