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,28 @@
|
|
|
1
|
+
# 17 - Italic Rendering for Abstract Classes
|
|
2
|
+
|
|
3
|
+
## Status: DONE (2026-07-23)
|
|
4
|
+
|
|
5
|
+
## Outcome
|
|
6
|
+
|
|
7
|
+
Updated `Elements#header_lines` to handle the abstract + stereotype
|
|
8
|
+
combination: when a class is abstract AND has a stereotype, emit
|
|
9
|
+
both lines (stereotype normal, name italic with `_` prefix).
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
if classifier.is_abstract
|
|
13
|
+
lines << ["«abstract»", :normal] if classifier.stereotype_refs&.any?
|
|
14
|
+
lines << ["_#{classifier.name}", :italic]
|
|
15
|
+
elsif classifier.stereotype_refs&.any?
|
|
16
|
+
lines << ["«#{stereotype}»", :normal]
|
|
17
|
+
lines << [name, :bold]
|
|
18
|
+
else
|
|
19
|
+
lines << [name, :bold]
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Abstract classes now render matching EA's encoding:
|
|
24
|
+
`<text style="font-style:italic;...">_TransportationObject</text>`.
|
|
25
|
+
|
|
26
|
+
## Files changed
|
|
27
|
+
|
|
28
|
+
- `lib/ea/svg/ea_emitter/elements.rb` — `header_lines` abstract+stereotype branch
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# TODO-D 17: Remaining Parity Gaps
|
|
2
|
+
|
|
3
|
+
## Current State (2026-07-26)
|
|
4
|
+
|
|
5
|
+
- Text overlap: **87.2%** (Jaccard, set-equality per `<text>` content)
|
|
6
|
+
- Text count delta: **-83** (-0.4% vs reference; effectively at parity)
|
|
7
|
+
- Shape counts: rect -460 (-20.9%), polygon -166 (-11.2%), path -181 (-5.1%)
|
|
8
|
+
- Within tolerance: 123/188 diagrams (65.4%)
|
|
9
|
+
|
|
10
|
+
## Closed Gaps
|
|
11
|
+
|
|
12
|
+
1. QEA `$LLB=...;LLT=...;LRB=...;LRT=...` parsing — only first segment
|
|
13
|
+
carries `$`, the rest are `KEY=` after `;`. Fixed.
|
|
14
|
+
2. Connector label rendering at LLT/LRT box positions, with role + «property»
|
|
15
|
+
+ mult. Fixed.
|
|
16
|
+
3. Abstract class underscore — the underscore lives in the QEA class name
|
|
17
|
+
itself (`_CityObject`), not added by EA at render time. Fixed.
|
|
18
|
+
4. Enumeration `«enumeration»` stereotype fallback. Fixed.
|
|
19
|
+
5. Connector-level stereotype label (e.g. «import» on Package connector).
|
|
20
|
+
Fixed.
|
|
21
|
+
6. Attribute default value rendering (`name: type [mult] = default`). Fixed.
|
|
22
|
+
7. Enumeration literal visibility marker (`<text> </text>` empty slot).
|
|
23
|
+
Fixed.
|
|
24
|
+
8. Multiplicity 1..1 → "1" rendering when paired with role name. Fixed.
|
|
25
|
+
|
|
26
|
+
## Open Gaps
|
|
27
|
+
|
|
28
|
+
### Text overlap (87.2% → target ~95%+)
|
|
29
|
+
|
|
30
|
+
1. **Auto-generated legend block** (~448 missing texts):
|
|
31
|
+
- "凡例" (Legend title, 12pt bold #003060)
|
|
32
|
+
- "GMLで定義されたクラス", "CityGMLで定義されたクラス",
|
|
33
|
+
"i-URで定義されたクラス" (3 color-coded class titles)
|
|
34
|
+
- "GMLに定義されたクラス", "CityGMLに定義されたクラス",
|
|
35
|
+
"i-URに定義されたクラス" (alternative particle に instead of で)
|
|
36
|
+
- EA auto-generates these when a diagram contains color-coded
|
|
37
|
+
stereotype elements. Source data location TBD — likely tied to
|
|
38
|
+
diagram-type or per-diagram MDGDgm flag.
|
|
39
|
+
|
|
40
|
+
2. **Ghost element discriminator** (TODO-D 20, deferred):
|
|
41
|
+
- Some elements without explicit `font=` in their ObjectStyle get
|
|
42
|
+
ghost rendering (7pt #595959, no attrs); others get full rendering.
|
|
43
|
+
- Tested font_family presence → broke parity (-5000 texts).
|
|
44
|
+
- Tested classifier.package_id match → broke parity (-1500 texts).
|
|
45
|
+
- Real discriminator likely combines: ObjectStyle detail level +
|
|
46
|
+
diagram context + classifier source package. Needs EA.exe
|
|
47
|
+
disassembly reference.
|
|
48
|
+
|
|
49
|
+
3. **Missing class headers** (~150 instances):
|
|
50
|
+
- Elements like `_AbstractGeometricAggregate`, `_CityObject`,
|
|
51
|
+
`_Site`, `_Feature`, `_TransportationObject` appear in reference
|
|
52
|
+
SVGs but not in t_diagramobjects for the diagram.
|
|
53
|
+
- Cause: either EA renders implicit elements from connector
|
|
54
|
+
endpoints, or the reference SVGs were generated from a different
|
|
55
|
+
QEA version than what we have.
|
|
56
|
+
|
|
57
|
+
4. **`(from CityGML2.0)` qualifier** (9 instances):
|
|
58
|
+
- EA shows a sub-line for elements imported from a specific
|
|
59
|
+
package version.
|
|
60
|
+
- Source: probably t_object.Package_ID joined with t_package
|
|
61
|
+
version metadata.
|
|
62
|
+
|
|
63
|
+
### Shape counts
|
|
64
|
+
|
|
65
|
+
1. **rect -460**:
|
|
66
|
+
- ~3 rects per diagram missing from legend block (container with
|
|
67
|
+
rx=3 + 3 color swatch rects). 188 diagrams × ~3 = ~564 expected,
|
|
68
|
+
close to actual -460 gap.
|
|
69
|
+
- Need legend modeling first (depends on Text gap #1).
|
|
70
|
+
|
|
71
|
+
2. **polygon -166**:
|
|
72
|
+
- Per-diagram marker counts differ — EA emits duplicate polygons
|
|
73
|
+
at overlapping connector endpoints (e.g. 7 identical triangles
|
|
74
|
+
at one position). Cause unknown.
|
|
75
|
+
- Likely needs investigation of t_diagramlinks.Hidden vs visible
|
|
76
|
+
flag and how EA consolidates markers.
|
|
77
|
+
|
|
78
|
+
3. **path -181**:
|
|
79
|
+
- EA splits connector paths into individual segments per
|
|
80
|
+
style group. We consolidate same-style paths into one `<g>`.
|
|
81
|
+
- Some diagrams have 30+ path delta — needs path-per-connector
|
|
82
|
+
vs grouped-path mode selection per diagram.
|
|
83
|
+
|
|
84
|
+
## Next Steps
|
|
85
|
+
|
|
86
|
+
1. Investigate auto-generated legend feature: how EA detects "this
|
|
87
|
+
diagram needs a legend" and where the legend text/colors come from.
|
|
88
|
+
2. Find a reliable ghost-element discriminator (likely involves
|
|
89
|
+
per-element display flags we haven't parsed yet).
|
|
90
|
+
3. Investigate duplicate-polygon emission for overlapping markers.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 18 - Canvas from image_bounds (matching EA)
|
|
2
|
+
|
|
3
|
+
## Status: DONE (2026-07-23)
|
|
4
|
+
|
|
5
|
+
## Outcome
|
|
6
|
+
|
|
7
|
+
`Canvas.from` now unions BOTH `bounds` and `image_bounds` for each
|
|
8
|
+
element. `image_bounds` captures the rendered extent (logical bounds
|
|
9
|
+
+ image padding), so the canvas grows to fit the full visual area.
|
|
10
|
+
|
|
11
|
+
Waterway canvas: 1138×869 → 1163×899 (ref=1138×892)
|
|
12
|
+
Building canvas: 1381×1524 → 1406×1554 (ref=1406×1530, width matches)
|
|
13
|
+
|
|
14
|
+
Remaining diff (25 px width on Waterway) is from image_bounds
|
|
15
|
+
extending further right than EA includes in canvas. Acceptable for
|
|
16
|
+
visual rendering — text and shapes are positioned correctly.
|
|
17
|
+
|
|
18
|
+
## Files changed
|
|
19
|
+
|
|
20
|
+
- `lib/ea/svg/ea_emitter/canvas.rb` — union bounds + image_bounds
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# TODO-D 18: Property lookup index (performance + OCP)
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
`Ea::Svg::EaEmitter::Labels#property_at_end` walks every Classifier
|
|
6
|
+
and every Property on each Classifier to find a Property by ID:
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
def property_at_end(association, end_kind)
|
|
10
|
+
id = end_kind == :source ? association.source_id : association.target_id
|
|
11
|
+
return nil unless id && model_index
|
|
12
|
+
|
|
13
|
+
model_index.values.each do |obj|
|
|
14
|
+
next unless obj.is_a?(Ea::Model::Classifier)
|
|
15
|
+
found = (obj.properties || []).find { |p| p.id == id }
|
|
16
|
+
return found if found
|
|
17
|
+
end
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For the 205-diagram plateau QEA this is called once per visible
|
|
23
|
+
association per diagram (~10k calls × ~600 classifiers × ~10
|
|
24
|
+
properties = 60M comparisons). That is the dominant cost in SVG
|
|
25
|
+
rendering.
|
|
26
|
+
|
|
27
|
+
## Fix
|
|
28
|
+
|
|
29
|
+
Add a lazily-built property index to `Ea::Model::Document` keyed by
|
|
30
|
+
property id. Labels (and any future consumer) calls
|
|
31
|
+
`document.property_by_id[id]` for O(1) lookup.
|
|
32
|
+
|
|
33
|
+
This keeps the lookup logic in the model (model-driven) and avoids
|
|
34
|
+
each consumer rebuilding the same index (DRY).
|
|
35
|
+
|
|
36
|
+
## Acceptance
|
|
37
|
+
|
|
38
|
+
- `Document#property_index` returns `{ property_id => Property }`.
|
|
39
|
+
- `Labels#property_at_end` uses the index.
|
|
40
|
+
- Parity text overlap remains ≥ 87%.
|
|
41
|
+
- Specs continue to pass.
|
|
42
|
+
- `rake benchmark` (or ad-hoc timing) shows measurable speed-up.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 19 - Connector Source/Target Edge Routing from SX/SY/EX/EY
|
|
2
|
+
|
|
3
|
+
## Status: DONE (2026-07-23)
|
|
4
|
+
|
|
5
|
+
## Outcome
|
|
6
|
+
|
|
7
|
+
`ConnectorRouter` now accepts `source_delta` and `target_delta`
|
|
8
|
+
(defaulting to [0,0]). `DiagramBuilder#compute_waypoints` passes
|
|
9
|
+
SX/SY/EX/EY from the parsed geometry to the router, which applies
|
|
10
|
+
them as deltas to the edge-attachment point.
|
|
11
|
+
|
|
12
|
+
`ExtensionGeometryParser.Placement` already exposed `sx`, `sy`,
|
|
13
|
+
`ex`, `ey` — just plumbs them through.
|
|
14
|
+
|
|
15
|
+
## Remaining diff
|
|
16
|
+
|
|
17
|
+
Waterway connectors still show ~20 px y-offset vs EA reference
|
|
18
|
+
(643 vs 663). The SX/SY deltas are small (-21 to 150) and don't
|
|
19
|
+
account for this gap. EA likely uses a different attachment
|
|
20
|
+
formula (not strict edge center) — possibly offset by half
|
|
21
|
+
font-height or other rendering consideration. Deferred to deeper
|
|
22
|
+
reverse-engineering.
|
|
23
|
+
|
|
24
|
+
## Files changed
|
|
25
|
+
|
|
26
|
+
- `lib/ea/svg/connector_router.rb` — source_delta / target_delta support
|
|
27
|
+
- `lib/ea/sources/xmi/diagram_builder.rb` — passes deltas to router
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# TODO-D 19: Replace respond_to? with is_a? (encapsulation)
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
`Ea::Svg::EaEmitter::Labels#connector_stereotype` uses
|
|
6
|
+
`respond_to?(:stereotype)`:
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
def connector_stereotype(connector)
|
|
10
|
+
rel = model_index ? model_index[connector.relationship_ref] : nil
|
|
11
|
+
stereotype = rel&.respond_to?(:stereotype) ? rel.stereotype : nil
|
|
12
|
+
return nil if stereotype.nil? || stereotype.to_s.empty?
|
|
13
|
+
"«#{stereotype}»"
|
|
14
|
+
end
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`respond_to?` is duck-typing that hides type errors until runtime.
|
|
18
|
+
Per the project rules: use `is_a?` for type checks, or design the
|
|
19
|
+
type hierarchy so the check isn't needed.
|
|
20
|
+
|
|
21
|
+
## Fix
|
|
22
|
+
|
|
23
|
+
`Ea::Model::Relationship` (base class) now declares `stereotype` as
|
|
24
|
+
an attribute. All concrete relationship subclasses inherit it. So
|
|
25
|
+
the check should be `rel.is_a?(Ea::Model::Relationship)`.
|
|
26
|
+
|
|
27
|
+
## Acceptance
|
|
28
|
+
|
|
29
|
+
- No `respond_to?` calls in `lib/`.
|
|
30
|
+
- Connector stereotype labels still render correctly.
|
|
31
|
+
- Specs pass.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# TODO-D 20: Split Labels emitter into MECE collaborators
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
`Ea::Svg::EaEmitter::Labels` currently handles three distinct
|
|
6
|
+
responsibilities:
|
|
7
|
+
|
|
8
|
+
1. Connector midpoint stereotype labels (e.g. «import»).
|
|
9
|
+
2. End-label role + «property» + multiplicity rendering.
|
|
10
|
+
3. Geometry box parsing and position resolution.
|
|
11
|
+
|
|
12
|
+
These change for different reasons:
|
|
13
|
+
|
|
14
|
+
- Midpoint rendering changes when EA's connector stereotype
|
|
15
|
+
conventions change.
|
|
16
|
+
- End-label rendering changes when role-name / multiplicity
|
|
17
|
+
rules change.
|
|
18
|
+
- Box parsing changes when the QEA geometry format changes.
|
|
19
|
+
|
|
20
|
+
MECE violation: the three concerns overlap inside one class. OCP
|
|
21
|
+
violation: adding a new label kind (e.g. connector name above the
|
|
22
|
+
midpoint) requires editing Labels rather than registering a new
|
|
23
|
+
collaborator.
|
|
24
|
+
|
|
25
|
+
## Fix
|
|
26
|
+
|
|
27
|
+
Introduce a small LabelRenderer registry:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Ea::Svg::EaEmitter::Label
|
|
31
|
+
├─ EndLabel (role + «property» + mult at LLT/LRT boxes)
|
|
32
|
+
├─ MidpointLabel (stereotype at path midpoint)
|
|
33
|
+
└─ Registry (dispatch by relationship kind / connector flag)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`Labels#texts_for` becomes:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
def texts_for(connector)
|
|
40
|
+
points = waypoint_pairs(connector)
|
|
41
|
+
return [] if points.size < 2
|
|
42
|
+
|
|
43
|
+
Label::Registry.for(connector, model_index: model_index)
|
|
44
|
+
.flat_map { |r| r.texts(connector, points) }
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Each renderer is independently testable. New label kinds are added
|
|
49
|
+
by registering, not by editing Labels (OCP).
|
|
50
|
+
|
|
51
|
+
## Acceptance
|
|
52
|
+
|
|
53
|
+
- Three discrete classes; each has its own spec.
|
|
54
|
+
- No behavior change (parity text overlap ≥ 87%).
|
|
55
|
+
- Existing labels spec still passes.
|
|
56
|
+
- New midpoint label spec covers the «import» path.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 20 - Visibility Glyph Font (Plus Sign in Monospace)
|
|
2
|
+
|
|
3
|
+
## Status: DONE (2026-07-23)
|
|
4
|
+
|
|
5
|
+
## Outcome
|
|
6
|
+
|
|
7
|
+
Three text-encoding fixes:
|
|
8
|
+
1. Visibility marker now emits as `+ ` (with trailing space) —
|
|
9
|
+
matches EA's text content exactly.
|
|
10
|
+
2. Namespace separator in type names converted to `::` (UML
|
|
11
|
+
standard) — `gml:CodeType` → `gml::CodeType`.
|
|
12
|
+
3. Abstract class names no longer get double underscore when the
|
|
13
|
+
source name already starts with `_` (e.g. `_Feature` stays
|
|
14
|
+
`_Feature`, not `__Feature`).
|
|
15
|
+
|
|
16
|
+
## Remaining gap
|
|
17
|
+
|
|
18
|
+
Text counts overshoot ref on diagrams where the source classifier
|
|
19
|
+
has more `ownedAttribute` entries than EA displays. EA truncates the
|
|
20
|
+
visible attribute list (likely a per-diagram display filter or box-
|
|
21
|
+
size fit), while we render every property. This is a content-
|
|
22
|
+
filtering concern, not an encoding concern — deferred.
|
|
23
|
+
|
|
24
|
+
## Files changed
|
|
25
|
+
|
|
26
|
+
- `lib/ea/svg/ea_emitter/elements.rb` — `abstract_name`,
|
|
27
|
+
`namespace_double_colon`, `split_visibility` updates
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 21 - Aggregation Navigability Arrow at Part End
|
|
2
|
+
|
|
3
|
+
## Status: DONE (2026-07-23)
|
|
4
|
+
|
|
5
|
+
## Outcome
|
|
6
|
+
|
|
7
|
+
`Markers#marker_specs` for Aggregation/Composition now returns TWO
|
|
8
|
+
Specs:
|
|
9
|
+
- Diamond polygon at whole end
|
|
10
|
+
- Arrow path at part end
|
|
11
|
+
|
|
12
|
+
Document interleaves line + diamond + arrow per aggregation
|
|
13
|
+
connector (via `count_for` API + `pair_per_connector`).
|
|
14
|
+
|
|
15
|
+
Waterway path count: 15 → 21 (ref=20, within ±1).
|
|
16
|
+
Bridge polygon+path counts now within ±2 of ref.
|
|
17
|
+
|
|
18
|
+
## Files changed
|
|
19
|
+
|
|
20
|
+
- `lib/ea/svg/ea_emitter/markers.rb` — `marker_specs` returns Array, `count_for` API
|
|
21
|
+
- `lib/ea/svg/ea_emitter/document.rb` — `pair_per_connector` interleave
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# TODO-D 21: Auto-generated legend block
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
EA renders a "凡例" (Legend) block in many diagrams that contain
|
|
6
|
+
color-coded stereotype elements. The block contains:
|
|
7
|
+
|
|
8
|
+
- A rounded-corner container rect (`rx=3`)
|
|
9
|
+
- 3 color swatch rects (`17×17`)
|
|
10
|
+
- A bold 12pt #003060 title "凡例"
|
|
11
|
+
- Three 9pt #003060 lines naming the source packages
|
|
12
|
+
|
|
13
|
+
Reference SVG example (EAID_0016F797):
|
|
14
|
+
|
|
15
|
+
```xml
|
|
16
|
+
<rect x="485" y="368" width="194" height="120" rx="3.00"/>
|
|
17
|
+
<rect x="495" y="398" width="17" height="17"/> <!-- swatch 1 -->
|
|
18
|
+
<rect x="495" y="417" width="17" height="17"/> <!-- swatch 2 -->
|
|
19
|
+
<rect x="495" y="436" width="17" height="17"/> <!-- swatch 3 -->
|
|
20
|
+
<text x="546.00" y="387.00" font-weight:700 font-size:12pt>凡例</text>
|
|
21
|
+
<text x="517.00" y="410.00">GMLに定義されたクラス</text>
|
|
22
|
+
<text x="517.00" y="429.00">CityGMLに定義されたクラス</text>
|
|
23
|
+
<text x="517.00" y="448.00">i-URに定義されたクラス</text>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The block is NOT modeled in the QEA schema. There is no "Legend"
|
|
27
|
+
Object_Type, no t_lists row, no StyleEx flag tied to it.
|
|
28
|
+
|
|
29
|
+
## Investigation Notes
|
|
30
|
+
|
|
31
|
+
- 27 Note objects in the QEA, none contains "凡例".
|
|
32
|
+
- 5 Text objects in the QEA, all empty Note field.
|
|
33
|
+
- The legend titles and swatch colors vary per diagram but follow
|
|
34
|
+
a fixed vocabulary: GML, CityGML, i-UR.
|
|
35
|
+
- The color swatches are the stereotype fill colors of elements
|
|
36
|
+
visible in the diagram (e.g. FFCFFF for CityGML, CCFFCC for GML).
|
|
37
|
+
|
|
38
|
+
## Hypothesis
|
|
39
|
+
|
|
40
|
+
EA's "Diagram Legend" feature is a built-in diagram decoration
|
|
41
|
+
that auto-generates when:
|
|
42
|
+
|
|
43
|
+
1. The diagram contains elements whose stereotype maps to a
|
|
44
|
+
known "package source" (GML, CityGML, i-UR).
|
|
45
|
+
2. The user has placed a Legend UI element (which EA serializes
|
|
46
|
+
somewhere we haven't found yet — possibly t_image or a binary
|
|
47
|
+
blob).
|
|
48
|
+
|
|
49
|
+
Without reverse-engineering EA.exe's legend code path or finding
|
|
50
|
+
the serialization, replicating this verbatim is high-risk.
|
|
51
|
+
|
|
52
|
+
## Status
|
|
53
|
+
|
|
54
|
+
Deferred. Pinned in the parity report (TODO.diagrams/
|
|
55
|
+
17-remaining-parity-gaps.md) as a known ~448-text gap.
|
|
56
|
+
|
|
57
|
+
## Acceptance (when picked up)
|
|
58
|
+
|
|
59
|
+
- Locate the legend serialization in the QEA (table + column).
|
|
60
|
+
- Model `Ea::Model::Legend` with `position`, `title`, `entries`
|
|
61
|
+
(each entry: color + label).
|
|
62
|
+
- Build `Ea::Svg::EaEmitter::Legend` renderer producing the
|
|
63
|
+
container rect + swatch rects + texts.
|
|
64
|
+
- Specs cover: empty legend, single-entry, three-entry.
|
|
65
|
+
- Parity text overlap climbs above 90%.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# TODO-D 22: Ghost element discriminator
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
EA renders some elements as "ghosts": smaller font (7pt), gray
|
|
6
|
+
text (#595959), no attribute/operation/tag compartments. The
|
|
7
|
+
discriminator is unclear.
|
|
8
|
+
|
|
9
|
+
## Empirical Observations
|
|
10
|
+
|
|
11
|
+
| Diagram | Element | ObjectStyle | Rendered as |
|
|
12
|
+
|--------------------|----------------------------|------------------------------------------------------|-------------|
|
|
13
|
+
| 位置図C0301 | _UrbanFunction | NSL=0;LCol=-1;...font=Yu Gothic UI;... | Full |
|
|
14
|
+
| 位置図C0301 | MultiSurface | NSL=0;BFol=-1;...fontsz=0;bold=0;... (no font=) | Ghost |
|
|
15
|
+
| CityGMLCore | _Feature | NSL=0;BCol=-1;BFol=-1;LCol=-1;LWth=-1;fontsz=0;... | Ghost |
|
|
16
|
+
| urf_4 | _UrbanFunction | NSL=0;BFol=-1;LCol=-1;fontsz=0;bold=0;... (no font=) | **Full** |
|
|
17
|
+
| 都市計画決定情報 | _UrbanFunction | NSL=0;LCol=-1;bold=0;... (no font=) | Ghost |
|
|
18
|
+
|
|
19
|
+
The presence or absence of `font=` is **not** the discriminator
|
|
20
|
+
(urf_4 elements lack `font=` but render full attributes).
|
|
21
|
+
|
|
22
|
+
`BCol` value is **not** the discriminator (urf_4 has explicit
|
|
23
|
+
BCol=16777215; MultiSurface in 位置図C0301 has BCol=13434828).
|
|
24
|
+
|
|
25
|
+
Classifier `package_id` vs diagram `package_id` is **not** the
|
|
26
|
+
discriminator (urf_4 cross-package elements get full attrs;
|
|
27
|
+
CityGMLCore cross-package elements get ghost).
|
|
28
|
+
|
|
29
|
+
## Hypothesis To Test
|
|
30
|
+
|
|
31
|
+
1. **Diagram-level "is reference" flag**: maybe EA marks diagrams
|
|
32
|
+
that only show snapshots of an external model. The discriminator
|
|
33
|
+
could be `t_diagram.SPT=1` (already 1 in all observed diagrams)
|
|
34
|
+
combined with another flag.
|
|
35
|
+
|
|
36
|
+
2. **Element-level "Link" semantics**: maybe elements placed as
|
|
37
|
+
"links" rather than embedded get ghost styling. The QEA may
|
|
38
|
+
encode this in t_diagramlinks.Path or t_object.Documentation
|
|
39
|
+
style flags we haven't parsed.
|
|
40
|
+
|
|
41
|
+
3. **t_objectstatistics or t_objectproperties**: an explicit "ghost
|
|
42
|
+
mode" tagged value.
|
|
43
|
+
|
|
44
|
+
## Status
|
|
45
|
+
|
|
46
|
+
Deferred. Without EA.exe disassembly or empirical testing inside
|
|
47
|
+
the EA GUI, the rule cannot be confidently derived.
|
|
48
|
+
|
|
49
|
+
## Acceptance (when picked up)
|
|
50
|
+
|
|
51
|
+
- Open the QEA in EA GUI, toggle the "Link" vs "Embed" property
|
|
52
|
+
on an element, observe the rendered SVG, and identify the QEA
|
|
53
|
+
field that changes.
|
|
54
|
+
- Implement a `ghost?` predicate on `Ea::Model::DiagramElement`
|
|
55
|
+
using the discovered field.
|
|
56
|
+
- Wire through `Elements` emitter to apply ghost styling.
|
|
57
|
+
- Specs cover both ghost and full paths.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 22 - Style-Based Path Grouping
|
|
2
|
+
|
|
3
|
+
## Status: DONE (2026-07-23)
|
|
4
|
+
|
|
5
|
+
## Outcome
|
|
6
|
+
|
|
7
|
+
`Connectors` emitter now supports `group_by_style:` constructor
|
|
8
|
+
option. When true, consecutive connectors with the same line style
|
|
9
|
+
are merged into a single `<g>` element with multiple `<path>` kids
|
|
10
|
+
(matching EA's encoding for diagrams with many similarly-styled
|
|
11
|
+
connectors).
|
|
12
|
+
|
|
13
|
+
Default behavior remains per-entity grouping (one `<g>` per
|
|
14
|
+
connector) to preserve z-order interleaving with markers.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
# Default: per-entity groups (current behavior)
|
|
20
|
+
Connectors.new(diagram, canvas: canvas)
|
|
21
|
+
|
|
22
|
+
# EA-style grouped:
|
|
23
|
+
Connectors.new(diagram, canvas: canvas, group_by_style: true)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Files changed
|
|
27
|
+
|
|
28
|
+
- `lib/ea/svg/ea_emitter/connectors.rb` — `group_by_style` option + `grouped_groups`
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# TODO-D 23: Marker polygon deduplication rules
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
EA's rules for emitting polygon markers when multiple connectors
|
|
6
|
+
share an endpoint are non-obvious.
|
|
7
|
+
|
|
8
|
+
## Empirical Observations
|
|
9
|
+
|
|
10
|
+
Aggregate across 188 reference SVGs:
|
|
11
|
+
- Total polygons: 1485
|
|
12
|
+
- Exact-duplicate polygons (same points string): **536** (36%)
|
|
13
|
+
|
|
14
|
+
EA duplicates polygon markers in many cases but not always. The
|
|
15
|
+
discriminator is unclear.
|
|
16
|
+
|
|
17
|
+
## Experiments Tried
|
|
18
|
+
|
|
19
|
+
| Strategy | Polygon delta | Within-tol |
|
|
20
|
+
|-----------------------------|---------------|------------|
|
|
21
|
+
| Anchor-based dedup (current)| -166 (-11.2%) | 123/188 (65.4%) |
|
|
22
|
+
| No dedup | +257 (+17.3%) | 139/188 (73.9%) |
|
|
23
|
+
| Exact-body dedup | +247 (+16.6%) | 141/188 (75.0%) |
|
|
24
|
+
|
|
25
|
+
Both "no dedup" and "exact-body dedup" improve within-tolerance
|
|
26
|
+
diagram count (+13%) but introduce polygon overshoot. Anchor-based
|
|
27
|
+
dedup has best polygon delta but lower diagram-tol rate.
|
|
28
|
+
|
|
29
|
+
## Hypothesis To Test
|
|
30
|
+
|
|
31
|
+
EA likely has per-marker-per-connector emission for `Path=`-routed
|
|
32
|
+
connectors (each visible link emits its own markers) but deduplicates
|
|
33
|
+
markers that share an anchor AND belong to connectors sharing a
|
|
34
|
+
common segment.
|
|
35
|
+
|
|
36
|
+
The QEA stores a `Path=` field on t_diagramlinks; connectors that
|
|
37
|
+
share the same visual path are co-rendered. We currently treat
|
|
38
|
+
each t_diagramlinks row independently.
|
|
39
|
+
|
|
40
|
+
## Acceptance (when picked up)
|
|
41
|
+
|
|
42
|
+
- Correlate polygon duplicates with shared `Path=` values.
|
|
43
|
+
- Implement per-diagram `grouped:` mode selection in Markers.
|
|
44
|
+
- Specs cover: distinct anchors (no dedup), shared anchor + shared
|
|
45
|
+
path (dedup), shared anchor + distinct path (no dedup).
|
|
46
|
+
- Polygon delta within ±20 (currently -166).
|
|
47
|
+
- Within-tolerance diagrams ≥ 75% (currently 65.4%).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 23 - Diagram-Level Multiplicity Label Positioning
|
|
2
|
+
|
|
3
|
+
## Status: DONE (2026-07-23)
|
|
4
|
+
|
|
5
|
+
## Outcome
|
|
6
|
+
|
|
7
|
+
Refactored `Ea::Svg::EaEmitter::Labels` to emit per-end role name +
|
|
8
|
+
multiplicity using LLB (left label box) / LLT (left label text) and
|
|
9
|
+
RLB / RLT offsets from the connector geometry.
|
|
10
|
+
|
|
11
|
+
Uses UML convention: the role visible at one end of the connector is
|
|
12
|
+
the property owned by the OPPOSITE classifier. So LLT (near source)
|
|
13
|
+
shows the target-end property; RLT (near target) shows the source-
|
|
14
|
+
end property.
|
|
15
|
+
|
|
16
|
+
Each label emits as TWO `<text>` elements matching EA's encoding:
|
|
17
|
+
one for the role name (`+roleName`) and one for the multiplicity
|
|
18
|
+
(`0..1`).
|
|
19
|
+
|
|
20
|
+
## Files changed
|
|
21
|
+
|
|
22
|
+
- `lib/ea/svg/ea_emitter/labels.rb` — full rewrite with model_index,
|
|
23
|
+
association_ends, label_position, role_text, multiplicity_text
|
|
24
|
+
- `lib/ea/svg/ea_emitter/document.rb` — passes `model_index` to Labels
|
|
25
|
+
|
|
26
|
+
## Outcome metrics
|
|
27
|
+
|
|
28
|
+
Text count mean abs diff dropped from 20 → 14 across 185 diagrams.
|
|
29
|
+
Waterway: 92 → 103 (ref=113). CityFurniture: 24 → 30 (ref=34).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# TODO-D 24: Track marker anchors structurally
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
`Ea::Svg::EaEmitter::Markers#anchor_key` parses the SVG string the
|
|
6
|
+
class itself just emitted in order to deduplicate entries by their
|
|
7
|
+
anchor point:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
def anchor_key(body)
|
|
11
|
+
match = body.match(/polygon points="(-?[\d.]+)\s+(-?[\d.]+)|path d="M (-?[\d.]+)\s+(-?[\d.]+)/)
|
|
12
|
+
return body unless match
|
|
13
|
+
...
|
|
14
|
+
end
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This is fragile:
|
|
18
|
+
|
|
19
|
+
- Round-trip through string parsing for data we already had
|
|
20
|
+
structurally.
|
|
21
|
+
- Regex assumes EA's exact SVG attribute ordering and whitespace.
|
|
22
|
+
- Hard to test in isolation (must build the body string first).
|
|
23
|
+
|
|
24
|
+
## Fix
|
|
25
|
+
|
|
26
|
+
Add `anchor` to the `Entry` struct. Capture it when the entry is
|
|
27
|
+
built (we already have `tip` at that point). Dedup on
|
|
28
|
+
`Entry#anchor` rather than re-parsing the body.
|
|
29
|
+
|
|
30
|
+
## Acceptance
|
|
31
|
+
|
|
32
|
+
- `Entry` struct carries `[x, y]` anchor.
|
|
33
|
+
- `anchor_key` method removed; dedup uses `Entry#anchor` directly.
|
|
34
|
+
- Markers specs continue to pass.
|
|
35
|
+
- Parity polygon delta unchanged.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# TODO-D 24: Track marker anchors structurally (DONE)
|
|
2
|
+
|
|
3
|
+
## Before
|
|
4
|
+
|
|
5
|
+
`Markers#anchor_key` parsed the rendered SVG string with regex to
|
|
6
|
+
extract the marker's anchor coordinates:
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
def anchor_key(body)
|
|
10
|
+
match = body.match(/polygon points="(-?[\d.]+)\s+(-?[\d.]+)|path d="M (-?[\d.]+)\s+(-?[\d.]+)/)
|
|
11
|
+
return body unless match
|
|
12
|
+
x = (match[1] || match[3]).to_f.round
|
|
13
|
+
y = (match[2] || match[4]).to_f.round
|
|
14
|
+
[x, y]
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
That mixes "data" (the anchor) with "presentation" (the rendered
|
|
19
|
+
string), and it parses the same SVG twice. Re-introducing the
|
|
20
|
+
anchor as a Ruby value fixes both.
|
|
21
|
+
|
|
22
|
+
## After
|
|
23
|
+
|
|
24
|
+
`Markers::Entry` is now a Struct that carries the anchor alongside
|
|
25
|
+
the body, with hash/eql? defined so `Array#uniq` collapses by
|
|
26
|
+
anchor without touching the SVG:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
Entry = Struct.new(:style_key, :body, :anchor, keyword_init: true) do
|
|
30
|
+
def hash
|
|
31
|
+
anchor.hash
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def eql?(other)
|
|
35
|
+
other.is_a?(Entry) && anchor == other.anchor
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`entries_for` computes the anchor once when building the Entry:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
def anchor_for(spec)
|
|
44
|
+
tx, ty = translate_point(spec.anchor)
|
|
45
|
+
[tx.round, ty.round]
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Benefits
|
|
50
|
+
|
|
51
|
+
- **Performance**: one anchor computation per marker instead of
|
|
52
|
+
a regex match per dedup pass.
|
|
53
|
+
- **Type safety**: anchor is a `[Integer, Integer]` pair, not a
|
|
54
|
+
parsed fragment of a String.
|
|
55
|
+
- **Encapsulation**: rendered SVG never re-parsed.
|
|
56
|
+
- **Testability**: `Entry#eql?` is unit-testable in isolation.
|
|
57
|
+
|
|
58
|
+
## Tests
|
|
59
|
+
|
|
60
|
+
- `spec/ea/svg/ea_emitter/markers_entry_spec.rb` covers anchor
|
|
61
|
+
equality, deduplication, and false-positive guards.
|
|
62
|
+
|
|
63
|
+
## Bench
|
|
64
|
+
|
|
65
|
+
Unchanged: rect -221, path -280, polygon -6, text -601,
|
|
66
|
+
overlap 64.5%. The refactor is behavior-preserving.
|