ea 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/02-polygon-over-rendering.md +29 -0
- data/TODO.diagrams/03-text-over-rendering.md +30 -0
- data/TODO.diagrams/04-rect-under-rendering.md +33 -0
- data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
- data/TODO.diagrams/06-group-layering-parity.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/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/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 +57 -2
- 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 +8 -15
- 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 +34 -0
- data/lib/ea/model/diagram_element.rb +16 -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/classifier_builder.rb +10 -1
- data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
- data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
- data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
- 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 +4 -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 +2 -3
- data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
- data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
- 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 +46 -12
- data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
- data/lib/ea/svg/ea_emitter/document.rb +15 -24
- 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 +283 -95
- 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 +93 -27
- 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 +351 -64
- 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 +33 -11
- 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.rb +2 -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 +449 -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,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
# Computes the (min_x, min_y, width, height) tuple for a
|
|
7
|
+
# Diagram's canvas. Encapsulates the union rules so Canvas
|
|
8
|
+
# stays a small value-object with just coordinate translation
|
|
9
|
+
# and formatting concerns.
|
|
10
|
+
#
|
|
11
|
+
# Sources contributing to the bounds:
|
|
12
|
+
# - ElementBounds: logical x-extent, logical+image y-extent
|
|
13
|
+
# - ConnectorBounds: waypoint positions
|
|
14
|
+
# - MarkerExtent: MARKER_EXTENT padding around end points
|
|
15
|
+
# - PackageTabExtent: extra space above Package elements for
|
|
16
|
+
# the tab polygon (which sits above the
|
|
17
|
+
# element's logical top).
|
|
18
|
+
#
|
|
19
|
+
# Each source is a method returning an Array<[x, y]> points.
|
|
20
|
+
# Composing new sources = adding a method, no modification of
|
|
21
|
+
# existing ones (OCP).
|
|
22
|
+
class BoundsCalculator
|
|
23
|
+
MARKER_EXTENT = 15
|
|
24
|
+
PACKAGE_TAB_HEIGHT = 20
|
|
25
|
+
# EA's canvas includes non-uniform frame insets around the
|
|
26
|
+
# element content. Reverse-engineered from reference SVG
|
|
27
|
+
# byte-diff: maintenance diagram has element 169x80 at
|
|
28
|
+
# logical (0,0), ref canvas is 254x177 with element at
|
|
29
|
+
# (35, 40). So:
|
|
30
|
+
# canvas_width = element_width + INSET_LEFT + INSET_RIGHT
|
|
31
|
+
# canvas_height = element_height + INSET_TOP + INSET_BOTTOM
|
|
32
|
+
# The top inset accommodates the frame tab + label space
|
|
33
|
+
# above the element.
|
|
34
|
+
INSET_LEFT = 35
|
|
35
|
+
INSET_RIGHT = 50
|
|
36
|
+
INSET_TOP = 40
|
|
37
|
+
INSET_BOTTOM = 57
|
|
38
|
+
|
|
39
|
+
attr_reader :diagram, :model_index
|
|
40
|
+
|
|
41
|
+
def initialize(diagram, model_index: nil)
|
|
42
|
+
@diagram = diagram
|
|
43
|
+
@model_index = model_index
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def compute
|
|
47
|
+
points = []
|
|
48
|
+
points.concat(element_points)
|
|
49
|
+
points.concat(connector_points)
|
|
50
|
+
points.concat(marker_points)
|
|
51
|
+
points.concat(package_tab_points)
|
|
52
|
+
return [0, 0, 1, 1] if points.empty?
|
|
53
|
+
|
|
54
|
+
xs = points.map(&:first)
|
|
55
|
+
ys = points.map(&:last)
|
|
56
|
+
min_x = xs.min || 0
|
|
57
|
+
min_y = ys.min || 0
|
|
58
|
+
[
|
|
59
|
+
min_x,
|
|
60
|
+
min_y,
|
|
61
|
+
(xs.max - min_x) + INSET_LEFT + INSET_RIGHT,
|
|
62
|
+
(ys.max - min_y) + INSET_TOP + INSET_BOTTOM
|
|
63
|
+
]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
# Element x-extent: logical bounds only (matches EA's canvas
|
|
69
|
+
# left/right exactly).
|
|
70
|
+
# Element y-extent: union of logical + image_bounds (image
|
|
71
|
+
# extends below bounds for shadow / image padding).
|
|
72
|
+
def element_points
|
|
73
|
+
pts = []
|
|
74
|
+
(diagram.elements || []).each do |e|
|
|
75
|
+
primary = e.bounds || e.image_bounds
|
|
76
|
+
if primary
|
|
77
|
+
pts << [primary.x, primary.y]
|
|
78
|
+
pts << [primary.x + primary.width, primary.y + primary.height]
|
|
79
|
+
end
|
|
80
|
+
if e.bounds && e.image_bounds
|
|
81
|
+
ib = e.image_bounds
|
|
82
|
+
pts << [ib.x, ib.y]
|
|
83
|
+
pts << [ib.x + ib.width, ib.y + ib.height]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
pts
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Package elements render with a tab polygon ABOVE the
|
|
90
|
+
# element's logical top edge (height PACKAGE_TAB_HEIGHT).
|
|
91
|
+
# Include those points so the canvas reserves space.
|
|
92
|
+
def package_tab_points
|
|
93
|
+
return [] if model_index.nil?
|
|
94
|
+
|
|
95
|
+
pts = []
|
|
96
|
+
(diagram.elements || []).each do |e|
|
|
97
|
+
ref = e.model_element_ref
|
|
98
|
+
next unless ref
|
|
99
|
+
|
|
100
|
+
candidate = model_index[ref]
|
|
101
|
+
next unless candidate.is_a?(Ea::Model::Package)
|
|
102
|
+
|
|
103
|
+
primary = e.bounds || e.image_bounds
|
|
104
|
+
next unless primary
|
|
105
|
+
|
|
106
|
+
pts << [primary.x, primary.y - PACKAGE_TAB_HEIGHT]
|
|
107
|
+
pts << [primary.x + primary.width, primary.y]
|
|
108
|
+
end
|
|
109
|
+
pts
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def connector_points
|
|
113
|
+
pts = []
|
|
114
|
+
(diagram.connectors || []).each do |c|
|
|
115
|
+
(c.waypoints || []).each do |wp|
|
|
116
|
+
next unless wp.position
|
|
117
|
+
|
|
118
|
+
pts << [wp.position.x, wp.position.y]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
pts
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def marker_points
|
|
125
|
+
pts = []
|
|
126
|
+
(diagram.connectors || []).each do |c|
|
|
127
|
+
waypoints = (c.waypoints || []).map(&:position).compact
|
|
128
|
+
next unless waypoints.size >= 2
|
|
129
|
+
|
|
130
|
+
source = waypoints.first
|
|
131
|
+
target = waypoints.last
|
|
132
|
+
pts << [source.x - MARKER_EXTENT, source.y - MARKER_EXTENT]
|
|
133
|
+
pts << [source.x + MARKER_EXTENT, source.y + MARKER_EXTENT]
|
|
134
|
+
pts << [target.x - MARKER_EXTENT, target.y - MARKER_EXTENT]
|
|
135
|
+
pts << [target.x + MARKER_EXTENT, target.y + MARKER_EXTENT]
|
|
136
|
+
end
|
|
137
|
+
pts
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -3,44 +3,47 @@
|
|
|
3
3
|
module Ea
|
|
4
4
|
module Svg
|
|
5
5
|
module EaEmitter
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
# Canvas carries the diagram's computed pixel bounds plus the
|
|
7
|
+
# coordinate-translation helpers that align element (0,0) with
|
|
8
|
+
# EA's content origin (35, 40). Bound computation itself is
|
|
9
|
+
# delegated to BoundsCalculator (SRP).
|
|
10
|
+
#
|
|
11
|
+
# Promoted from a Struct.new + `do ... end` block to a proper
|
|
12
|
+
# class so the public API is explicit (attr_reader vs implicit
|
|
13
|
+
# struct fields) and behavior is named rather than implicit.
|
|
14
|
+
# Struct definitions with instance methods conflate "data" and
|
|
15
|
+
# "behavior" — a class keeps them separable while the values
|
|
16
|
+
# stay immutable.
|
|
17
|
+
class Canvas
|
|
18
|
+
# EA's diagram canvas places element (0,0) at SVG (35, 40) —
|
|
19
|
+
# the diagram's content origin inset from the canvas top-left
|
|
20
|
+
# corner. Reverse-engineered from reference SVG byte-diff
|
|
21
|
+
# against the maintenance diagram.
|
|
22
|
+
PX_PER_CM = 37.795275591
|
|
23
|
+
FRAME_INSET_LEFT = 35
|
|
24
|
+
FRAME_INSET_TOP = 40
|
|
12
25
|
|
|
13
|
-
|
|
14
|
-
points = []
|
|
15
|
-
(diagram.elements || []).each do |e|
|
|
16
|
-
b = e.image_bounds || e.bounds
|
|
17
|
-
next unless b
|
|
26
|
+
attr_reader :min_x, :min_y, :width, :height
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
points << [wp.position.x, wp.position.y]
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
return new(min_x: 0, min_y: 0, width: 1, height: 1) if points.empty?
|
|
28
|
+
def initialize(min_x:, min_y:, width:, height:)
|
|
29
|
+
@min_x = min_x
|
|
30
|
+
@min_y = min_y
|
|
31
|
+
@width = width
|
|
32
|
+
@height = height
|
|
33
|
+
end
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
35
|
+
# Build a Canvas for a diagram by running the bound
|
|
36
|
+
# calculator. Defaults model_index to nil (no canvas-aware
|
|
37
|
+
# element bounds needed) for callers that only need a
|
|
38
|
+
# frame-size estimate.
|
|
39
|
+
def self.from(diagram, model_index: nil)
|
|
40
|
+
min_x, min_y, width, height =
|
|
41
|
+
BoundsCalculator.new(diagram, model_index: model_index).compute
|
|
42
|
+
new(min_x: min_x, min_y: min_y, width: width, height: height)
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
def view_box
|
|
43
|
-
"
|
|
46
|
+
"0 0 #{width} #{height}"
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
def width_cm
|
|
@@ -51,6 +54,26 @@ module Ea
|
|
|
51
54
|
format_cm(height)
|
|
52
55
|
end
|
|
53
56
|
|
|
57
|
+
# Translate a content-space point into SVG-space. EA insets
|
|
58
|
+
# the content area from the canvas top-left by
|
|
59
|
+
# FRAME_INSET_LEFT/TOP.
|
|
60
|
+
def translate_x(x)
|
|
61
|
+
x - min_x + FRAME_INSET_LEFT
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def translate_y(y)
|
|
65
|
+
y - min_y + FRAME_INSET_TOP
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Compact coordinate formatter. Integers lose the decimal
|
|
69
|
+
# point; floats keep up to two digits with trailing zeros
|
|
70
|
+
# stripped (matches EA's emitted numeric format).
|
|
71
|
+
def self.coord(value)
|
|
72
|
+
return value.to_i.to_s if value == value.to_i
|
|
73
|
+
|
|
74
|
+
format("%.2f", value).sub(/\.?0+$/, "")
|
|
75
|
+
end
|
|
76
|
+
|
|
54
77
|
private
|
|
55
78
|
|
|
56
79
|
def format_cm(px)
|
|
@@ -62,4 +85,4 @@ module Ea
|
|
|
62
85
|
end
|
|
63
86
|
end
|
|
64
87
|
end
|
|
65
|
-
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
# Single source of truth for fill + stroke color decisions.
|
|
7
|
+
#
|
|
8
|
+
# Theme :119 applies:
|
|
9
|
+
# - Border color (#9A8484) for element strokes
|
|
10
|
+
# - Attribute color (#66413F) for attribute text
|
|
11
|
+
# - Method color for operation text
|
|
12
|
+
# Theme :119 does NOT apply:
|
|
13
|
+
# - Per-type fill colors (elements use default #FFFFFF)
|
|
14
|
+
# - Text color (#000000 black for non-attribute text)
|
|
15
|
+
#
|
|
16
|
+
# Precedence chain for fill:
|
|
17
|
+
# 1. Element's BCol int (when present and not sentinel)
|
|
18
|
+
# 2. Default fill (#FFFFFF)
|
|
19
|
+
#
|
|
20
|
+
# Precedence chain for stroke:
|
|
21
|
+
# 1. Element's LCol int (when present and not sentinel)
|
|
22
|
+
# 2. Theme border color (when theme is themed)
|
|
23
|
+
# 3. Default stroke (#000000)
|
|
24
|
+
class ColorResolver
|
|
25
|
+
DEFAULT_FILL = "#FFFFFF"
|
|
26
|
+
DEFAULT_STROKE = "#000000"
|
|
27
|
+
|
|
28
|
+
attr_reader :theme, :stereotype_resolver
|
|
29
|
+
|
|
30
|
+
def initialize(theme: nil, stereotype_resolver: Ea::Svg::StereotypeColorResolver.new)
|
|
31
|
+
@theme = theme || Ea::Theme::Registry.default
|
|
32
|
+
@stereotype_resolver = stereotype_resolver
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fill_for(element, classifier)
|
|
36
|
+
bcol = Element::BColDecoder.to_hex(element&.background_color)
|
|
37
|
+
return bcol if bcol
|
|
38
|
+
|
|
39
|
+
DEFAULT_FILL
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def stroke_for(element)
|
|
43
|
+
lcol = Element::BColDecoder.to_hex(element&.line_color)
|
|
44
|
+
return lcol if lcol
|
|
45
|
+
return theme.border_color if theme.themed?
|
|
46
|
+
|
|
47
|
+
DEFAULT_STROKE
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def attribute_text_color
|
|
51
|
+
theme.themed? ? (theme.attribute_color || DEFAULT_STROKE) : DEFAULT_STROKE
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Compartment
|
|
7
|
+
# Attribute compartment. Skipped when no attribute lines.
|
|
8
|
+
module Attributes
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def render(context)
|
|
12
|
+
return nil if context.attr_lines.empty?
|
|
13
|
+
|
|
14
|
+
Element::AttributeRenderer.render(
|
|
15
|
+
context.attr_lines,
|
|
16
|
+
bounds: context.bounds,
|
|
17
|
+
first_y: context.geometry.attr_first_y,
|
|
18
|
+
family: context.family, size: context.size, size_unit: context.size_unit,
|
|
19
|
+
fill: context.theme.attribute_text_color,
|
|
20
|
+
visibility_x_offset: context.theme.attribute_spec.visibility_x_offset,
|
|
21
|
+
content_x_offset: context.theme.attribute_spec.content_x_offset
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Compartment
|
|
7
|
+
# Constraints compartment. Skipped when the classifier has no
|
|
8
|
+
# constraints. Renders an italic "constraints" header and one
|
|
9
|
+
# "{name}" line per constraint.
|
|
10
|
+
module Constraints
|
|
11
|
+
DEFAULT_TEXT_COLOR = "#000000"
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def render(context)
|
|
16
|
+
return nil unless context.constraints&.any?
|
|
17
|
+
|
|
18
|
+
Element::ConstraintRenderer.render(
|
|
19
|
+
context.constraints,
|
|
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,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Compartment
|
|
7
|
+
# Enumeration literal compartment: divider line + "literals"
|
|
8
|
+
# italic header + each literal name. Skipped when the
|
|
9
|
+
# classifier has no literals or the geometry cannot fit them.
|
|
10
|
+
module EnumLiterals
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def render(context)
|
|
14
|
+
return nil unless context.enum_literals.any?
|
|
15
|
+
return nil unless context.geometry.enum_literal_first_y
|
|
16
|
+
|
|
17
|
+
[
|
|
18
|
+
Element::DividerRenderer.render(
|
|
19
|
+
context.bounds,
|
|
20
|
+
y: context.geometry.enum_divider_y,
|
|
21
|
+
stroke: context.stroke,
|
|
22
|
+
stroke_width: context.stroke_width
|
|
23
|
+
),
|
|
24
|
+
render_literals_block(context)
|
|
25
|
+
].join("\n")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def render_literals_block(context)
|
|
29
|
+
line_h = context.size + (context.theme.compartments.attr_line_offset || 4)
|
|
30
|
+
text_blocks = [literal_header_text(context)]
|
|
31
|
+
context.enum_literals.each_with_index do |literal, idx|
|
|
32
|
+
y = context.geometry.enum_literal_first_y + ((idx + 1) * line_h)
|
|
33
|
+
text_blocks << visibility_placeholder(context, y)
|
|
34
|
+
text_blocks << literal_name_text(context, literal, y)
|
|
35
|
+
end
|
|
36
|
+
wrap_group(text_blocks, context.theme.text_color)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def literal_header_text(context)
|
|
40
|
+
TextRenderer.new(
|
|
41
|
+
content: "literals",
|
|
42
|
+
x: context.bounds.x + (context.bounds.width / 2.0) -
|
|
43
|
+
(8 * context.size * context.theme.text_width_factor) / 2,
|
|
44
|
+
y: context.geometry.enum_literal_first_y,
|
|
45
|
+
family: context.family, size: context.size, size_unit: context.size_unit,
|
|
46
|
+
style: "italic", fill: context.theme.text_color,
|
|
47
|
+
stroke_in_text: context.theme.stroke_in_text_color,
|
|
48
|
+
width_factor: context.theme.text_width_factor
|
|
49
|
+
).to_svg
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def visibility_placeholder(context, y)
|
|
53
|
+
TextRenderer.new(
|
|
54
|
+
content: " ",
|
|
55
|
+
x: context.bounds.x + (context.theme.attribute_spec.visibility_x_offset || 5),
|
|
56
|
+
y: y,
|
|
57
|
+
family: context.family, size: context.size, size_unit: context.size_unit,
|
|
58
|
+
fill: context.theme.text_color,
|
|
59
|
+
stroke_in_text: context.theme.stroke_in_text_color,
|
|
60
|
+
width_factor: context.theme.text_width_factor
|
|
61
|
+
).to_svg
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def literal_name_text(context, literal, y)
|
|
65
|
+
TextRenderer.new(
|
|
66
|
+
content: literal.name.to_s,
|
|
67
|
+
x: context.bounds.x + (context.theme.attribute_spec.content_x_offset || 26),
|
|
68
|
+
y: y,
|
|
69
|
+
family: context.family, size: context.size, size_unit: context.size_unit,
|
|
70
|
+
fill: context.theme.text_color,
|
|
71
|
+
stroke_in_text: context.theme.stroke_in_text_color,
|
|
72
|
+
width_factor: context.theme.text_width_factor
|
|
73
|
+
).to_svg
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def wrap_group(text_blocks, text_color)
|
|
77
|
+
style = "stroke-width:1;stroke-linecap:round;stroke-linejoin:bevel; " \
|
|
78
|
+
"fill:#{text_color};fill-opacity:1.00; " \
|
|
79
|
+
"stroke:#000000; stroke-opacity:0.00"
|
|
80
|
+
%(<g style="#{style}">\n#{text_blocks.join("\n")}\n</g>)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Compartment
|
|
7
|
+
# Header compartment: stereotype label + class name. Skips
|
|
8
|
+
# rendering when the classifier produces no header lines.
|
|
9
|
+
module Header
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def render(context)
|
|
13
|
+
return nil if context.header_lines.empty?
|
|
14
|
+
|
|
15
|
+
Element::HeaderRenderer.render(
|
|
16
|
+
context.header_lines,
|
|
17
|
+
bounds: context.bounds,
|
|
18
|
+
first_y: context.geometry.header_first_y,
|
|
19
|
+
family: context.family, size: context.size, size_unit: context.size_unit,
|
|
20
|
+
fill: context.theme.text_color,
|
|
21
|
+
weight_normal: context.theme.text_weight_normal,
|
|
22
|
+
weight_bold: context.theme.text_weight_bold,
|
|
23
|
+
stroke_in_text: context.theme.stroke_in_text_color,
|
|
24
|
+
width_factor: context.theme.text_width_factor,
|
|
25
|
+
line_offset: context.theme.compartments.header_line_offset
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Compartment
|
|
7
|
+
# Horizontal divider between header and the first content
|
|
8
|
+
# compartment (attributes / operations / literals / tagged
|
|
9
|
+
# values). Skipped when there is no content below.
|
|
10
|
+
module HeaderDivider
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def render(context)
|
|
14
|
+
return nil unless context.geometry
|
|
15
|
+
return nil unless context.geometry.divider_y
|
|
16
|
+
return nil unless divider_required?(context)
|
|
17
|
+
|
|
18
|
+
Element::DividerRenderer.render(
|
|
19
|
+
context.bounds,
|
|
20
|
+
y: context.geometry.divider_y,
|
|
21
|
+
stroke: context.stroke,
|
|
22
|
+
stroke_width: context.stroke_width
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# EA shows a header divider on Object diagrams when the
|
|
27
|
+
# element is large enough to hold a slot compartment (height
|
|
28
|
+
# > 50). The starter Object diagram uses 90×50 boxes with
|
|
29
|
+
# no slots and no divider; richer Object diagrams use
|
|
30
|
+
# 133×53+ boxes with the divider always present. On
|
|
31
|
+
# HandDraw diagrams (Domain Model), every element shows a
|
|
32
|
+
# divider regardless of content. For other diagrams the
|
|
33
|
+
# divider is shown only when there is content below the
|
|
34
|
+
# header.
|
|
35
|
+
def divider_required?(context)
|
|
36
|
+
return true if hand_draw?(context)
|
|
37
|
+
return object_element_with_slots?(context) if object_diagram?(context)
|
|
38
|
+
|
|
39
|
+
context.content_below_header?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def object_diagram?(context)
|
|
43
|
+
context.diagram && context.diagram.diagram_type == "Object"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def hand_draw?(context)
|
|
47
|
+
context.diagram && context.diagram.hand_draw
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def object_element_with_slots?(context)
|
|
51
|
+
context.bounds && context.bounds.height.to_i > 50
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Svg
|
|
5
|
+
module EaEmitter
|
|
6
|
+
module Compartment
|
|
7
|
+
# Instance slot compartment. Renders each slot on an
|
|
8
|
+
# InstanceSpecification as "name op value", then appends
|
|
9
|
+
# "(from PackageName)" in italics below the slots when the
|
|
10
|
+
# instance has a classifier. Skipped entirely when the model
|
|
11
|
+
# element is not an InstanceSpecification or has neither
|
|
12
|
+
# slots nor a classifier.
|
|
13
|
+
module InstanceSlots
|
|
14
|
+
ROW_OFFSET = 13
|
|
15
|
+
SUBTITLE_EXTRA_OFFSET = 10
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
def render(context)
|
|
20
|
+
return nil unless instance?(context)
|
|
21
|
+
|
|
22
|
+
instance = context.model_element
|
|
23
|
+
slots = instance.slots || []
|
|
24
|
+
subtitle = from_package_subtitle(instance)
|
|
25
|
+
return nil if slots.empty? && subtitle.nil?
|
|
26
|
+
|
|
27
|
+
first_y = context.geometry.attr_first_y
|
|
28
|
+
bounds = context.bounds
|
|
29
|
+
fill = context.theme.attribute_text_color
|
|
30
|
+
blocks = []
|
|
31
|
+
slots.each_with_index do |slot, idx|
|
|
32
|
+
y = first_y + (idx * ROW_OFFSET)
|
|
33
|
+
blocks << line_text(slot, bounds, y, context, fill)
|
|
34
|
+
end
|
|
35
|
+
if subtitle
|
|
36
|
+
y = first_y + (slots.size * ROW_OFFSET) + SUBTITLE_EXTRA_OFFSET
|
|
37
|
+
blocks << subtitle_text(subtitle, bounds, y, context, fill)
|
|
38
|
+
end
|
|
39
|
+
wrap(blocks.join("\n"), fill)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def instance?(context)
|
|
43
|
+
context.model_element.is_a?(Ea::Model::InstanceSpecification)
|
|
44
|
+
end
|
|
45
|
+
module_function :instance?
|
|
46
|
+
|
|
47
|
+
def from_package_subtitle(instance)
|
|
48
|
+
pkg = instance.package_name
|
|
49
|
+
return nil if pkg.nil? || pkg.empty?
|
|
50
|
+
|
|
51
|
+
classifier = instance.classifier_name
|
|
52
|
+
return nil if classifier.nil? || classifier.empty?
|
|
53
|
+
|
|
54
|
+
"(from #{pkg})"
|
|
55
|
+
end
|
|
56
|
+
module_function :from_package_subtitle
|
|
57
|
+
|
|
58
|
+
def line_text(slot, bounds, y_pos, context, fill)
|
|
59
|
+
content = "#{slot.name} #{slot.op} #{slot.value}"
|
|
60
|
+
TextRenderer.new(
|
|
61
|
+
content: content,
|
|
62
|
+
x: bounds.x + (context.theme.attribute_spec.content_x_offset || 26),
|
|
63
|
+
y: y_pos,
|
|
64
|
+
family: context.family, size: context.size,
|
|
65
|
+
size_unit: context.size_unit, fill: fill
|
|
66
|
+
).to_svg
|
|
67
|
+
end
|
|
68
|
+
module_function :line_text
|
|
69
|
+
|
|
70
|
+
def subtitle_text(text, bounds, y_pos, context, fill)
|
|
71
|
+
estimated_width = text.length * context.size * 0.5
|
|
72
|
+
x_pos = bounds.x + (bounds.width - estimated_width) / 2.0
|
|
73
|
+
TextRenderer.new(
|
|
74
|
+
content: text,
|
|
75
|
+
x: x_pos,
|
|
76
|
+
y: y_pos,
|
|
77
|
+
family: context.family, size: context.size,
|
|
78
|
+
size_unit: context.size_unit, fill: fill,
|
|
79
|
+
style: "italic"
|
|
80
|
+
).to_svg
|
|
81
|
+
end
|
|
82
|
+
module_function :subtitle_text
|
|
83
|
+
|
|
84
|
+
def wrap(body, fill)
|
|
85
|
+
group_style = "stroke-width:1;stroke-linecap:round;" \
|
|
86
|
+
"stroke-linejoin:bevel; fill:#{fill};" \
|
|
87
|
+
"fill-opacity:1.00; stroke:#000000;" \
|
|
88
|
+
" stroke-opacity:0.00"
|
|
89
|
+
%(<g style="#{group_style}">\n#{body}\n</g>)
|
|
90
|
+
end
|
|
91
|
+
module_function :wrap
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|