etassia 0.1.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 +7 -0
- data/CHANGELOG.md +140 -0
- data/LICENSE +25 -0
- data/README.adoc +436 -0
- data/exe/etassia +7 -0
- data/lib/etassia/cli.rb +187 -0
- data/lib/etassia/commands/batch.rb +210 -0
- data/lib/etassia/commands/notation_loader.rb +22 -0
- data/lib/etassia/commands/render.rb +98 -0
- data/lib/etassia/commands/types.rb +33 -0
- data/lib/etassia/commands/version.rb +24 -0
- data/lib/etassia/corpus_type_names.rb +52 -0
- data/lib/etassia/diagram/architecture.rb +108 -0
- data/lib/etassia/diagram/base.rb +61 -0
- data/lib/etassia/diagram/block.rb +102 -0
- data/lib/etassia/diagram/c4.rb +337 -0
- data/lib/etassia/diagram/class_diagram.rb +466 -0
- data/lib/etassia/diagram/class_namespace.rb +17 -0
- data/lib/etassia/diagram/class_note.rb +17 -0
- data/lib/etassia/diagram/containment.rb +107 -0
- data/lib/etassia/diagram/er_diagram.rb +317 -0
- data/lib/etassia/diagram/error.rb +38 -0
- data/lib/etassia/diagram/flowchart.rb +419 -0
- data/lib/etassia/diagram/flowchart_label_text.rb +63 -0
- data/lib/etassia/diagram/gantt.rb +94 -0
- data/lib/etassia/diagram/generic_text.rb +30 -0
- data/lib/etassia/diagram/git_graph.rb +57 -0
- data/lib/etassia/diagram/info.rb +38 -0
- data/lib/etassia/diagram/kanban.rb +198 -0
- data/lib/etassia/diagram/mindmap.rb +71 -0
- data/lib/etassia/diagram/packet.rb +93 -0
- data/lib/etassia/diagram/pie.rb +115 -0
- data/lib/etassia/diagram/quadrant.rb +135 -0
- data/lib/etassia/diagram/radar.rb +65 -0
- data/lib/etassia/diagram/requirement.rb +168 -0
- data/lib/etassia/diagram/sankey.rb +217 -0
- data/lib/etassia/diagram/sequence.rb +299 -0
- data/lib/etassia/diagram/sequence_box.rb +18 -0
- data/lib/etassia/diagram/sequence_frame.rb +37 -0
- data/lib/etassia/diagram/sequence_frame_section.rb +23 -0
- data/lib/etassia/diagram/sequence_text.rb +69 -0
- data/lib/etassia/diagram/sequence_wrap.rb +23 -0
- data/lib/etassia/diagram/state_diagram.rb +282 -0
- data/lib/etassia/diagram/timeline.rb +183 -0
- data/lib/etassia/diagram/treemap.rb +95 -0
- data/lib/etassia/diagram/user_journey.rb +153 -0
- data/lib/etassia/diagram/xy_chart.rb +90 -0
- data/lib/etassia/diagram.rb +8 -0
- data/lib/etassia/diagram_registry.rb +34 -0
- data/lib/etassia/engine.rb +334 -0
- data/lib/etassia/error/diagram_type_error.rb +10 -0
- data/lib/etassia/error/layout_error.rb +12 -0
- data/lib/etassia/error/notation_registration_error.rb +10 -0
- data/lib/etassia/error/parse_error.rb +11 -0
- data/lib/etassia/error/pipeline_error.rb +10 -0
- data/lib/etassia/error/render_error.rb +12 -0
- data/lib/etassia/error/score_error.rb +12 -0
- data/lib/etassia/error.rb +10 -0
- data/lib/etassia/error_report.rb +69 -0
- data/lib/etassia/ir/data.rb +39 -0
- data/lib/etassia/ir/data_value.rb +35 -0
- data/lib/etassia/ir/dimension.rb +10 -0
- data/lib/etassia/ir/edge.rb +34 -0
- data/lib/etassia/ir/graph.rb +28 -0
- data/lib/etassia/ir/integrity.rb +80 -0
- data/lib/etassia/ir/item.rb +46 -0
- data/lib/etassia/ir/model.rb +56 -0
- data/lib/etassia/ir/node.rb +10 -0
- data/lib/etassia/ir/placement.rb +47 -0
- data/lib/etassia/ir/prepositioned.rb +25 -0
- data/lib/etassia/ir/prepositioned_item.rb +26 -0
- data/lib/etassia/ir/property_set.rb +43 -0
- data/lib/etassia/ir/scalar.rb +25 -0
- data/lib/etassia/ir/series.rb +10 -0
- data/lib/etassia/ir.rb +17 -0
- data/lib/etassia/js_math.rb +511 -0
- data/lib/etassia/js_number.rb +64 -0
- data/lib/etassia/layout/architecture.rb +1472 -0
- data/lib/etassia/layout/base.rb +276 -0
- data/lib/etassia/layout/block.rb +738 -0
- data/lib/etassia/layout/c4.rb +876 -0
- data/lib/etassia/layout/c4_boundary_metrics.rb +35 -0
- data/lib/etassia/layout/c4_bounds.rb +106 -0
- data/lib/etassia/layout/c4_intersection.rb +77 -0
- data/lib/etassia/layout/c4_placement.rb +135 -0
- data/lib/etassia/layout/c4_shape_metrics.rb +97 -0
- data/lib/etassia/layout/c4_shape_outline.rb +67 -0
- data/lib/etassia/layout/c4_stereotype.rb +30 -0
- data/lib/etassia/layout/c4_text.rb +54 -0
- data/lib/etassia/layout/class_cell_placement.rb +148 -0
- data/lib/etassia/layout/class_diagram.rb +1116 -0
- data/lib/etassia/layout/class_diagram_namespaces.rb +26 -0
- data/lib/etassia/layout/class_diagram_notes.rb +26 -0
- data/lib/etassia/layout/class_name_advances.rb +31 -0
- data/lib/etassia/layout/class_namespace_box.rb +21 -0
- data/lib/etassia/layout/class_namespace_nodes.rb +197 -0
- data/lib/etassia/layout/class_namespace_placement.rb +71 -0
- data/lib/etassia/layout/class_note.rb +44 -0
- data/lib/etassia/layout/class_note_nodes.rb +84 -0
- data/lib/etassia/layout/elk_notices.rb +48 -0
- data/lib/etassia/layout/elk_placement.rb +197 -0
- data/lib/etassia/layout/er_diagram.rb +1002 -0
- data/lib/etassia/layout/er_diagram_adjacency.rb +20 -0
- data/lib/etassia/layout/er_diagram_alignment.rb +72 -0
- data/lib/etassia/layout/er_diagram_compaction.rb +85 -0
- data/lib/etassia/layout/er_diagram_conflicts.rb +70 -0
- data/lib/etassia/layout/er_diagram_coordinates.rb +104 -0
- data/lib/etassia/layout/er_diagram_entity_size.rb +82 -0
- data/lib/etassia/layout/er_diagram_layering.rb +109 -0
- data/lib/etassia/layout/er_diagram_ordering.rb +143 -0
- data/lib/etassia/layout/er_diagram_placement.rb +163 -0
- data/lib/etassia/layout/er_diagram_placement_node.rb +22 -0
- data/lib/etassia/layout/error.rb +133 -0
- data/lib/etassia/layout/flowchart/brandes_koepf.rb +208 -0
- data/lib/etassia/layout/flowchart/cluster_reach.rb +70 -0
- data/lib/etassia/layout/flowchart/component_packing.rb +129 -0
- data/lib/etassia/layout/flowchart/directed_placement.rb +428 -0
- data/lib/etassia/layout/flowchart/edge_chains.rb +111 -0
- data/lib/etassia/layout/flowchart/edge_order.rb +34 -0
- data/lib/etassia/layout/flowchart/edge_router.rb +533 -0
- data/lib/etassia/layout/flowchart/label_backdrop_geometry.rb +62 -0
- data/lib/etassia/layout/flowchart/layer_spacing.rb +85 -0
- data/lib/etassia/layout/flowchart/level_edges.rb +74 -0
- data/lib/etassia/layout/flowchart/level_frame.rb +47 -0
- data/lib/etassia/layout/flowchart/minimum_rank_spacing.rb +74 -0
- data/lib/etassia/layout/flowchart/row_centering.rb +229 -0
- data/lib/etassia/layout/flowchart.rb +1290 -0
- data/lib/etassia/layout/gantt.rb +814 -0
- data/lib/etassia/layout/gantt_duration.rb +49 -0
- data/lib/etassia/layout/gantt_exclusions.rb +74 -0
- data/lib/etassia/layout/gantt_ticks.rb +165 -0
- data/lib/etassia/layout/git_graph.rb +1075 -0
- data/lib/etassia/layout/grid.rb +165 -0
- data/lib/etassia/layout/info.rb +113 -0
- data/lib/etassia/layout/kanban.rb +452 -0
- data/lib/etassia/layout/kanban_card_text.rb +95 -0
- data/lib/etassia/layout/mindmap.rb +425 -0
- data/lib/etassia/layout/mindmap_cose.rb +81 -0
- data/lib/etassia/layout/mindmap_cose_body.rb +93 -0
- data/lib/etassia/layout/mindmap_cose_geometry.rb +190 -0
- data/lib/etassia/layout/mindmap_cose_grid.rb +96 -0
- data/lib/etassia/layout/mindmap_cose_link.rb +42 -0
- data/lib/etassia/layout/mindmap_cose_relaxation.rb +166 -0
- data/lib/etassia/layout/mindmap_cose_seed.rb +93 -0
- data/lib/etassia/layout/mindmap_cose_wedge.rb +37 -0
- data/lib/etassia/layout/mindmap_node_size.rb +130 -0
- data/lib/etassia/layout/packet.rb +285 -0
- data/lib/etassia/layout/pie.rb +305 -0
- data/lib/etassia/layout/pie_legend.rb +64 -0
- data/lib/etassia/layout/pie_legend_entry.rb +21 -0
- data/lib/etassia/layout/quadrant.rb +402 -0
- data/lib/etassia/layout/radar.rb +442 -0
- data/lib/etassia/layout/requirement.rb +765 -0
- data/lib/etassia/layout/sankey.rb +501 -0
- data/lib/etassia/layout/scene.rb +18 -0
- data/lib/etassia/layout/sequence/actor_gaps.rb +151 -0
- data/lib/etassia/layout/sequence/box_shape.rb +23 -0
- data/lib/etassia/layout/sequence/creations.rb +73 -0
- data/lib/etassia/layout/sequence/frame_divider.rb +22 -0
- data/lib/etassia/layout/sequence/frame_placement.rb +424 -0
- data/lib/etassia/layout/sequence/frame_reader.rb +75 -0
- data/lib/etassia/layout/sequence/frame_shape.rb +45 -0
- data/lib/etassia/layout/sequence/geometry.rb +24 -0
- data/lib/etassia/layout/sequence/message_rows.rb +165 -0
- data/lib/etassia/layout/sequence/note.rb +16 -0
- data/lib/etassia/layout/sequence/note_box.rb +114 -0
- data/lib/etassia/layout/sequence/note_placement.rb +223 -0
- data/lib/etassia/layout/sequence/text_width.rb +69 -0
- data/lib/etassia/layout/sequence/text_wrap.rb +93 -0
- data/lib/etassia/layout/sequence.rb +936 -0
- data/lib/etassia/layout/state_diagram.rb +1277 -0
- data/lib/etassia/layout/timeline.rb +165 -0
- data/lib/etassia/layout/timeline_card.rb +142 -0
- data/lib/etassia/layout/timeline_flow.rb +164 -0
- data/lib/etassia/layout/timeline_line.rb +33 -0
- data/lib/etassia/layout/timeline_rounding.rb +19 -0
- data/lib/etassia/layout/timeline_text.rb +105 -0
- data/lib/etassia/layout/treemap.rb +303 -0
- data/lib/etassia/layout/treemap_placement.rb +52 -0
- data/lib/etassia/layout/treemap_squarify.rb +108 -0
- data/lib/etassia/layout/user_journey.rb +300 -0
- data/lib/etassia/layout/user_journey_face.rb +33 -0
- data/lib/etassia/layout/user_journey_face_geometry.rb +80 -0
- data/lib/etassia/layout/user_journey_geometry.rb +336 -0
- data/lib/etassia/layout/user_journey_legend.rb +84 -0
- data/lib/etassia/layout/xy_chart.rb +620 -0
- data/lib/etassia/layout.rb +19 -0
- data/lib/etassia/lint_debt_scoreboard.rb +219 -0
- data/lib/etassia/markdown_text/emphasis_simulator.rb +373 -0
- data/lib/etassia/markdown_text.rb +393 -0
- data/lib/etassia/notation/builtin.rb +39 -0
- data/lib/etassia/notation/entry.rb +15 -0
- data/lib/etassia/notation/mermaid/class_diagram_config.rb +42 -0
- data/lib/etassia/notation/mermaid/git_graph_config.rb +46 -0
- data/lib/etassia/notation/mermaid/ir_adapter.rb +27 -0
- data/lib/etassia/notation/mermaid/ir_adapters/architecture.rb +196 -0
- data/lib/etassia/notation/mermaid/ir_adapters/block.rb +138 -0
- data/lib/etassia/notation/mermaid/ir_adapters/c4.rb +258 -0
- data/lib/etassia/notation/mermaid/ir_adapters/class_diagram.rb +257 -0
- data/lib/etassia/notation/mermaid/ir_adapters/er_diagram.rb +220 -0
- data/lib/etassia/notation/mermaid/ir_adapters/error.rb +32 -0
- data/lib/etassia/notation/mermaid/ir_adapters/flowchart.rb +208 -0
- data/lib/etassia/notation/mermaid/ir_adapters/gantt.rb +236 -0
- data/lib/etassia/notation/mermaid/ir_adapters/git_graph.rb +186 -0
- data/lib/etassia/notation/mermaid/ir_adapters/info.rb +34 -0
- data/lib/etassia/notation/mermaid/ir_adapters/kanban.rb +110 -0
- data/lib/etassia/notation/mermaid/ir_adapters/mindmap.rb +76 -0
- data/lib/etassia/notation/mermaid/ir_adapters/packet.rb +51 -0
- data/lib/etassia/notation/mermaid/ir_adapters/pie.rb +71 -0
- data/lib/etassia/notation/mermaid/ir_adapters/quadrant.rb +116 -0
- data/lib/etassia/notation/mermaid/ir_adapters/radar.rb +142 -0
- data/lib/etassia/notation/mermaid/ir_adapters/requirement.rb +237 -0
- data/lib/etassia/notation/mermaid/ir_adapters/sankey.rb +74 -0
- data/lib/etassia/notation/mermaid/ir_adapters/sequence.rb +304 -0
- data/lib/etassia/notation/mermaid/ir_adapters/state_diagram.rb +225 -0
- data/lib/etassia/notation/mermaid/ir_adapters/timeline.rb +93 -0
- data/lib/etassia/notation/mermaid/ir_adapters/treemap.rb +143 -0
- data/lib/etassia/notation/mermaid/ir_adapters/user_journey.rb +127 -0
- data/lib/etassia/notation/mermaid/ir_adapters/xychart.rb +131 -0
- data/lib/etassia/notation/mermaid.rb +399 -0
- data/lib/etassia/notation/parsed.rb +31 -0
- data/lib/etassia/notation/plantuml/activity/arrow_head.rb +45 -0
- data/lib/etassia/notation/plantuml/activity/arrow_style.rb +53 -0
- data/lib/etassia/notation/plantuml/activity/bar_columns.rb +75 -0
- data/lib/etassia/notation/plantuml/activity/box.rb +60 -0
- data/lib/etassia/notation/plantuml/activity/builder.rb +77 -0
- data/lib/etassia/notation/plantuml/activity/canvas.rb +117 -0
- data/lib/etassia/notation/plantuml/activity/column_centres.rb +151 -0
- data/lib/etassia/notation/plantuml/activity/columns.rb +224 -0
- data/lib/etassia/notation/plantuml/activity/diagram.rb +35 -0
- data/lib/etassia/notation/plantuml/activity/edge.rb +35 -0
- data/lib/etassia/notation/plantuml/activity/edge_marks.rb +68 -0
- data/lib/etassia/notation/plantuml/activity/exits.rb +61 -0
- data/lib/etassia/notation/plantuml/activity/frame_marks.rb +116 -0
- data/lib/etassia/notation/plantuml/activity/lane_steps.rb +82 -0
- data/lib/etassia/notation/plantuml/activity/lane_titles.rb +46 -0
- data/lib/etassia/notation/plantuml/activity/layout.rb +50 -0
- data/lib/etassia/notation/plantuml/activity/legacy_parser.rb +181 -0
- data/lib/etassia/notation/plantuml/activity/legacy_row_gaps.rb +65 -0
- data/lib/etassia/notation/plantuml/activity/mark.rb +23 -0
- data/lib/etassia/notation/plantuml/activity/marks.rb +62 -0
- data/lib/etassia/notation/plantuml/activity/metrics.rb +169 -0
- data/lib/etassia/notation/plantuml/activity/new_parser/blocks.rb +166 -0
- data/lib/etassia/notation/plantuml/activity/new_parser.rb +177 -0
- data/lib/etassia/notation/plantuml/activity/node.rb +50 -0
- data/lib/etassia/notation/plantuml/activity/node_marks.rb +174 -0
- data/lib/etassia/notation/plantuml/activity/note.rb +27 -0
- data/lib/etassia/notation/plantuml/activity/parser.rb +51 -0
- data/lib/etassia/notation/plantuml/activity/placement.rb +327 -0
- data/lib/etassia/notation/plantuml/activity/ranking.rb +142 -0
- data/lib/etassia/notation/plantuml/activity/reach.rb +58 -0
- data/lib/etassia/notation/plantuml/activity/renderer.rb +220 -0
- data/lib/etassia/notation/plantuml/activity/route.rb +22 -0
- data/lib/etassia/notation/plantuml/activity/router.rb +308 -0
- data/lib/etassia/notation/plantuml/activity/row_gaps.rb +105 -0
- data/lib/etassia/notation/plantuml/activity/row_spread.rb +59 -0
- data/lib/etassia/notation/plantuml/activity/scene.rb +20 -0
- data/lib/etassia/notation/plantuml/activity/text.rb +36 -0
- data/lib/etassia/notation/plantuml/activity.rb +50 -0
- data/lib/etassia/notation/plantuml/arrow.rb +135 -0
- data/lib/etassia/notation/plantuml/caption.rb +50 -0
- data/lib/etassia/notation/plantuml/caption_layout.rb +97 -0
- data/lib/etassia/notation/plantuml/caption_rows.rb +67 -0
- data/lib/etassia/notation/plantuml/class_header.rb +178 -0
- data/lib/etassia/notation/plantuml/class_name.rb +51 -0
- data/lib/etassia/notation/plantuml/component.rb +37 -0
- data/lib/etassia/notation/plantuml/creole.rb +278 -0
- data/lib/etassia/notation/plantuml/creole_icons.rb +33 -0
- data/lib/etassia/notation/plantuml/diagram.rb +60 -0
- data/lib/etassia/notation/plantuml/diagram_builder.rb +442 -0
- data/lib/etassia/notation/plantuml/diagram_name.rb +51 -0
- data/lib/etassia/notation/plantuml/directives.rb +33 -0
- data/lib/etassia/notation/plantuml/drawing/box.rb +28 -0
- data/lib/etassia/notation/plantuml/drawing/face.rb +39 -0
- data/lib/etassia/notation/plantuml/drawing/pen.rb +105 -0
- data/lib/etassia/notation/plantuml/drawing/renderer.rb +36 -0
- data/lib/etassia/notation/plantuml/drawing/scene.rb +28 -0
- data/lib/etassia/notation/plantuml/drawing.rb +19 -0
- data/lib/etassia/notation/plantuml/ebnf/blocks.rb +272 -0
- data/lib/etassia/notation/plantuml/ebnf/layout.rb +117 -0
- data/lib/etassia/notation/plantuml/ebnf/model.rb +22 -0
- data/lib/etassia/notation/plantuml/ebnf/reader.rb +131 -0
- data/lib/etassia/notation/plantuml/ebnf.rb +34 -0
- data/lib/etassia/notation/plantuml/end_texts.rb +78 -0
- data/lib/etassia/notation/plantuml/entity.rb +54 -0
- data/lib/etassia/notation/plantuml/further.rb +38 -0
- data/lib/etassia/notation/plantuml/gantt/layout.rb +269 -0
- data/lib/etassia/notation/plantuml/gantt/reader.rb +210 -0
- data/lib/etassia/notation/plantuml/gantt/renderer.rb +81 -0
- data/lib/etassia/notation/plantuml/gantt.rb +34 -0
- data/lib/etassia/notation/plantuml/include/blocks.rb +66 -0
- data/lib/etassia/notation/plantuml/include/finder.rb +61 -0
- data/lib/etassia/notation/plantuml/include.rb +78 -0
- data/lib/etassia/notation/plantuml/ir_adapter/annotations.rb +40 -0
- data/lib/etassia/notation/plantuml/ir_adapter/links.rb +91 -0
- data/lib/etassia/notation/plantuml/ir_adapter/sink.rb +49 -0
- data/lib/etassia/notation/plantuml/ir_adapter/structure.rb +86 -0
- data/lib/etassia/notation/plantuml/ir_adapter.rb +32 -0
- data/lib/etassia/notation/plantuml/ir_reader/annotations.rb +51 -0
- data/lib/etassia/notation/plantuml/ir_reader/index.rb +49 -0
- data/lib/etassia/notation/plantuml/ir_reader/links.rb +133 -0
- data/lib/etassia/notation/plantuml/ir_reader/structure.rb +83 -0
- data/lib/etassia/notation/plantuml/ir_reader.rb +42 -0
- data/lib/etassia/notation/plantuml/junction.rb +20 -0
- data/lib/etassia/notation/plantuml/klass.rb +41 -0
- data/lib/etassia/notation/plantuml/label_clearance.rb +67 -0
- data/lib/etassia/notation/plantuml/layout.rb +2806 -0
- data/lib/etassia/notation/plantuml/member.rb +37 -0
- data/lib/etassia/notation/plantuml/member_icon.rb +87 -0
- data/lib/etassia/notation/plantuml/member_icon_elements.rb +91 -0
- data/lib/etassia/notation/plantuml/member_pitch.rb +76 -0
- data/lib/etassia/notation/plantuml/member_rule.rb +48 -0
- data/lib/etassia/notation/plantuml/network/grid.rb +170 -0
- data/lib/etassia/notation/plantuml/network/layout.rb +158 -0
- data/lib/etassia/notation/plantuml/network/model.rb +22 -0
- data/lib/etassia/notation/plantuml/network/placement.rb +64 -0
- data/lib/etassia/notation/plantuml/network/reader.rb +126 -0
- data/lib/etassia/notation/plantuml/network/shapes.rb +109 -0
- data/lib/etassia/notation/plantuml/network.rb +37 -0
- data/lib/etassia/notation/plantuml/node_graph/archimate.rb +49 -0
- data/lib/etassia/notation/plantuml/node_graph/box_reader.rb +86 -0
- data/lib/etassia/notation/plantuml/node_graph/cloud.rb +73 -0
- data/lib/etassia/notation/plantuml/node_graph/crows_foot.rb +85 -0
- data/lib/etassia/notation/plantuml/node_graph/curve.rb +66 -0
- data/lib/etassia/notation/plantuml/node_graph/data_reader.rb +77 -0
- data/lib/etassia/notation/plantuml/node_graph/data_shape.rb +39 -0
- data/lib/etassia/notation/plantuml/node_graph/detour.rb +98 -0
- data/lib/etassia/notation/plantuml/node_graph/edge.rb +26 -0
- data/lib/etassia/notation/plantuml/node_graph/entity_metrics.rb +49 -0
- data/lib/etassia/notation/plantuml/node_graph/entity_reader.rb +65 -0
- data/lib/etassia/notation/plantuml/node_graph/entity_shape.rb +111 -0
- data/lib/etassia/notation/plantuml/node_graph/figures.rb +92 -0
- data/lib/etassia/notation/plantuml/node_graph/frame_ports.rb +73 -0
- data/lib/etassia/notation/plantuml/node_graph/graph.rb +40 -0
- data/lib/etassia/notation/plantuml/node_graph/heads.rb +89 -0
- data/lib/etassia/notation/plantuml/node_graph/label_fan.rb +76 -0
- data/lib/etassia/notation/plantuml/node_graph/label_placer.rb +310 -0
- data/lib/etassia/notation/plantuml/node_graph/label_waypoint.rb +47 -0
- data/lib/etassia/notation/plantuml/node_graph/layout.rb +293 -0
- data/lib/etassia/notation/plantuml/node_graph/link.rb +78 -0
- data/lib/etassia/notation/plantuml/node_graph/macros.rb +39 -0
- data/lib/etassia/notation/plantuml/node_graph/metrics.rb +113 -0
- data/lib/etassia/notation/plantuml/node_graph/node.rb +44 -0
- data/lib/etassia/notation/plantuml/node_graph/note_tail.rb +58 -0
- data/lib/etassia/notation/plantuml/node_graph/packer.rb +126 -0
- data/lib/etassia/notation/plantuml/node_graph/parser.rb +309 -0
- data/lib/etassia/notation/plantuml/node_graph/paths.rb +48 -0
- data/lib/etassia/notation/plantuml/node_graph/ranking.rb +231 -0
- data/lib/etassia/notation/plantuml/node_graph/reader.rb +53 -0
- data/lib/etassia/notation/plantuml/node_graph/references.rb +38 -0
- data/lib/etassia/notation/plantuml/node_graph/renderer.rb +160 -0
- data/lib/etassia/notation/plantuml/node_graph/scene.rb +22 -0
- data/lib/etassia/notation/plantuml/node_graph/shapes.rb +302 -0
- data/lib/etassia/notation/plantuml/node_graph/skipped_ranks.rb +61 -0
- data/lib/etassia/notation/plantuml/node_graph/tab.rb +68 -0
- data/lib/etassia/notation/plantuml/node_graph/usecase_ellipse.rb +100 -0
- data/lib/etassia/notation/plantuml/node_graph.rb +38 -0
- data/lib/etassia/notation/plantuml/note.rb +27 -0
- data/lib/etassia/notation/plantuml/note_layout.rb +257 -0
- data/lib/etassia/notation/plantuml/note_outline.rb +68 -0
- data/lib/etassia/notation/plantuml/objects/map_rows.rb +46 -0
- data/lib/etassia/notation/plantuml/objects/reader.rb +208 -0
- data/lib/etassia/notation/plantuml/objects.rb +39 -0
- data/lib/etassia/notation/plantuml/package.rb +45 -0
- data/lib/etassia/notation/plantuml/package_color.rb +32 -0
- data/lib/etassia/notation/plantuml/package_frames.rb +178 -0
- data/lib/etassia/notation/plantuml/parser.rb +622 -0
- data/lib/etassia/notation/plantuml/portion_visibility.rb +91 -0
- data/lib/etassia/notation/plantuml/qualified_association_layout.rb +332 -0
- data/lib/etassia/notation/plantuml/qualifier_box.rb +78 -0
- data/lib/etassia/notation/plantuml/rank_chain.rb +527 -0
- data/lib/etassia/notation/plantuml/rank_hierarchy.rb +347 -0
- data/lib/etassia/notation/plantuml/relation.rb +103 -0
- data/lib/etassia/notation/plantuml/relation_label_separator.rb +199 -0
- data/lib/etassia/notation/plantuml/relation_path_points.rb +84 -0
- data/lib/etassia/notation/plantuml/renderer.rb +490 -0
- data/lib/etassia/notation/plantuml/scene.rb +134 -0
- data/lib/etassia/notation/plantuml/segment_box.rb +85 -0
- data/lib/etassia/notation/plantuml/sequence/activation.rb +28 -0
- data/lib/etassia/notation/plantuml/sequence/appearance.rb +71 -0
- data/lib/etassia/notation/plantuml/sequence/arrow_end.rb +23 -0
- data/lib/etassia/notation/plantuml/sequence/arrow_marks.rb +111 -0
- data/lib/etassia/notation/plantuml/sequence/arrow_style.rb +42 -0
- data/lib/etassia/notation/plantuml/sequence/arrow_syntax.rb +122 -0
- data/lib/etassia/notation/plantuml/sequence/bar_tracker.rb +59 -0
- data/lib/etassia/notation/plantuml/sequence/box.rb +23 -0
- data/lib/etassia/notation/plantuml/sequence/chrome.rb +60 -0
- data/lib/etassia/notation/plantuml/sequence/chrome_rows.rb +184 -0
- data/lib/etassia/notation/plantuml/sequence/create.rb +34 -0
- data/lib/etassia/notation/plantuml/sequence/delay.rb +19 -0
- data/lib/etassia/notation/plantuml/sequence/destroy.rb +20 -0
- data/lib/etassia/notation/plantuml/sequence/diagram.rb +86 -0
- data/lib/etassia/notation/plantuml/sequence/divider.rb +19 -0
- data/lib/etassia/notation/plantuml/sequence/edge.rb +53 -0
- data/lib/etassia/notation/plantuml/sequence/embedded.rb +77 -0
- data/lib/etassia/notation/plantuml/sequence/fill.rb +56 -0
- data/lib/etassia/notation/plantuml/sequence/fragment.rb +28 -0
- data/lib/etassia/notation/plantuml/sequence/fragment_shape.rb +138 -0
- data/lib/etassia/notation/plantuml/sequence/head_style.rb +43 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/blocks.rb +73 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/fills.rb +24 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/items.rb +48 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/messages.rb +62 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/participants.rb +42 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/settings.rb +54 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter.rb +38 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/blocks.rb +77 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/fills.rb +27 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/index.rb +36 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/items.rb +35 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/messages.rb +70 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/participants.rb +42 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/settings.rb +74 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader.rb +38 -0
- data/lib/etassia/notation/plantuml/sequence/keyword_defaults.rb +28 -0
- data/lib/etassia/notation/plantuml/sequence/layout.rb +1567 -0
- data/lib/etassia/notation/plantuml/sequence/message.rb +97 -0
- data/lib/etassia/notation/plantuml/sequence/message_wrap.rb +77 -0
- data/lib/etassia/notation/plantuml/sequence/note.rb +61 -0
- data/lib/etassia/notation/plantuml/sequence/note_fill.rb +35 -0
- data/lib/etassia/notation/plantuml/sequence/note_geometry.rb +126 -0
- data/lib/etassia/notation/plantuml/sequence/note_shape.rb +51 -0
- data/lib/etassia/notation/plantuml/sequence/numbered_label.rb +57 -0
- data/lib/etassia/notation/plantuml/sequence/numbering.rb +49 -0
- data/lib/etassia/notation/plantuml/sequence/outline.rb +204 -0
- data/lib/etassia/notation/plantuml/sequence/page_break.rb +17 -0
- data/lib/etassia/notation/plantuml/sequence/paint.rb +79 -0
- data/lib/etassia/notation/plantuml/sequence/parallel_message.rb +19 -0
- data/lib/etassia/notation/plantuml/sequence/parser.rb +844 -0
- data/lib/etassia/notation/plantuml/sequence/participant.rb +27 -0
- data/lib/etassia/notation/plantuml/sequence/picture.rb +29 -0
- data/lib/etassia/notation/plantuml/sequence/ref.rb +24 -0
- data/lib/etassia/notation/plantuml/sequence/ref_shape.rb +74 -0
- data/lib/etassia/notation/plantuml/sequence/refusals.rb +47 -0
- data/lib/etassia/notation/plantuml/sequence/renderer.rb +501 -0
- data/lib/etassia/notation/plantuml/sequence/scene.rb +200 -0
- data/lib/etassia/notation/plantuml/sequence/skin.rb +76 -0
- data/lib/etassia/notation/plantuml/sequence/spacing.rb +28 -0
- data/lib/etassia/notation/plantuml/sequence/style.rb +151 -0
- data/lib/etassia/notation/plantuml/sequence/tab_size.rb +42 -0
- data/lib/etassia/notation/plantuml/sequence/walker.rb +1190 -0
- data/lib/etassia/notation/plantuml/sequence.rb +47 -0
- data/lib/etassia/notation/plantuml/source_lines.rb +33 -0
- data/lib/etassia/notation/plantuml/state/axis_solver.rb +272 -0
- data/lib/etassia/notation/plantuml/state/cluster_padding.rb +75 -0
- data/lib/etassia/notation/plantuml/state/curve.rb +73 -0
- data/lib/etassia/notation/plantuml/state/dot_axis.rb +248 -0
- data/lib/etassia/notation/plantuml/state/extent.rb +42 -0
- data/lib/etassia/notation/plantuml/state/hierarchy.rb +355 -0
- data/lib/etassia/notation/plantuml/state/label_clearance.rb +148 -0
- data/lib/etassia/notation/plantuml/state/label_reach.rb +139 -0
- data/lib/etassia/notation/plantuml/state/layout.rb +173 -0
- data/lib/etassia/notation/plantuml/state/level_edges.rb +113 -0
- data/lib/etassia/notation/plantuml/state/look.rb +36 -0
- data/lib/etassia/notation/plantuml/state/note_shape.rb +73 -0
- data/lib/etassia/notation/plantuml/state/plane.rb +85 -0
- data/lib/etassia/notation/plantuml/state/ranking.rb +181 -0
- data/lib/etassia/notation/plantuml/state/reader.rb +310 -0
- data/lib/etassia/notation/plantuml/state/renderer.rb +253 -0
- data/lib/etassia/notation/plantuml/state/route_detour.rb +115 -0
- data/lib/etassia/notation/plantuml/state/route_geometry.rb +186 -0
- data/lib/etassia/notation/plantuml/state/routes.rb +196 -0
- data/lib/etassia/notation/plantuml/state/row.rb +88 -0
- data/lib/etassia/notation/plantuml/state/rows.rb +50 -0
- data/lib/etassia/notation/plantuml/state/stretch.rb +71 -0
- data/lib/etassia/notation/plantuml/state.rb +62 -0
- data/lib/etassia/notation/plantuml/stereotype_head.rb +58 -0
- data/lib/etassia/notation/plantuml/style/bound.rb +34 -0
- data/lib/etassia/notation/plantuml/style/colour.rb +41 -0
- data/lib/etassia/notation/plantuml/style/colour_names.rb +89 -0
- data/lib/etassia/notation/plantuml/style/defaults.rb +33 -0
- data/lib/etassia/notation/plantuml/style/inline.rb +56 -0
- data/lib/etassia/notation/plantuml/style/reader.rb +242 -0
- data/lib/etassia/notation/plantuml/style/sheet.rb +103 -0
- data/lib/etassia/notation/plantuml/style.rb +32 -0
- data/lib/etassia/notation/plantuml/style_reader.rb +160 -0
- data/lib/etassia/notation/plantuml/style_sheet.rb +27 -0
- data/lib/etassia/notation/plantuml/styled_relation_curve.rb +71 -0
- data/lib/etassia/notation/plantuml/timing/arrows.rb +81 -0
- data/lib/etassia/notation/plantuml/timing/axis.rb +100 -0
- data/lib/etassia/notation/plantuml/timing/layout.rb +172 -0
- data/lib/etassia/notation/plantuml/timing/model.rb +34 -0
- data/lib/etassia/notation/plantuml/timing/reader.rb +173 -0
- data/lib/etassia/notation/plantuml/timing/signals.rb +328 -0
- data/lib/etassia/notation/plantuml/timing.rb +38 -0
- data/lib/etassia/notation/plantuml/tree/layout.rb +276 -0
- data/lib/etassia/notation/plantuml/tree/mindmap_layout.rb +157 -0
- data/lib/etassia/notation/plantuml/tree/reader.rb +186 -0
- data/lib/etassia/notation/plantuml/tree/renderer.rb +67 -0
- data/lib/etassia/notation/plantuml/tree/role.rb +57 -0
- data/lib/etassia/notation/plantuml/tree/rules.rb +156 -0
- data/lib/etassia/notation/plantuml/tree/wbs_layout.rb +164 -0
- data/lib/etassia/notation/plantuml/tree.rb +41 -0
- data/lib/etassia/notation/plantuml/unsupported_construct_error.rb +39 -0
- data/lib/etassia/notation/plantuml/unsupported_constructs.rb +103 -0
- data/lib/etassia/notation/plantuml/usecase.rb +36 -0
- data/lib/etassia/notation/plantuml.rb +132 -0
- data/lib/etassia/notation/plantuml_data/boxes/frames.rb +101 -0
- data/lib/etassia/notation/plantuml_data/boxes/graph_layout.rb +348 -0
- data/lib/etassia/notation/plantuml_data/boxes/heads.rb +65 -0
- data/lib/etassia/notation/plantuml_data/boxes/highlights.rb +70 -0
- data/lib/etassia/notation/plantuml_data/boxes/inline_rows.rb +99 -0
- data/lib/etassia/notation/plantuml_data/boxes/json_reader.rb +24 -0
- data/lib/etassia/notation/plantuml_data/boxes/link_text.rb +94 -0
- data/lib/etassia/notation/plantuml_data/boxes/marks.rb +76 -0
- data/lib/etassia/notation/plantuml_data/boxes/ornaments.rb +130 -0
- data/lib/etassia/notation/plantuml_data/boxes/renderer.rb +95 -0
- data/lib/etassia/notation/plantuml_data/boxes/row_alignment.rb +58 -0
- data/lib/etassia/notation/plantuml_data/boxes/row_bend.rb +43 -0
- data/lib/etassia/notation/plantuml_data/boxes/scene.rb +27 -0
- data/lib/etassia/notation/plantuml_data/boxes/table.rb +56 -0
- data/lib/etassia/notation/plantuml_data/boxes/table_box.rb +355 -0
- data/lib/etassia/notation/plantuml_data/boxes/text.rb +101 -0
- data/lib/etassia/notation/plantuml_data/boxes/tree_builder.rb +82 -0
- data/lib/etassia/notation/plantuml_data/boxes/tree_layout.rb +82 -0
- data/lib/etassia/notation/plantuml_data/boxes/yaml_reader.rb +105 -0
- data/lib/etassia/notation/plantuml_data/openiconic.LICENSE +6 -0
- data/lib/etassia/notation/plantuml_data/openiconic.json +1 -0
- data/lib/etassia/notation/plantuml_data.rb +84 -0
- data/lib/etassia/notation/plantuml_salt/block.rb +80 -0
- data/lib/etassia/notation/plantuml_salt/box.rb +30 -0
- data/lib/etassia/notation/plantuml_salt/font_metrics.rb +28 -0
- data/lib/etassia/notation/plantuml_salt/grid_marks.rb +66 -0
- data/lib/etassia/notation/plantuml_salt/group_marks.rb +77 -0
- data/lib/etassia/notation/plantuml_salt/layout.rb +206 -0
- data/lib/etassia/notation/plantuml_salt/mark.rb +36 -0
- data/lib/etassia/notation/plantuml_salt/markup.rb +57 -0
- data/lib/etassia/notation/plantuml_salt/parser.rb +239 -0
- data/lib/etassia/notation/plantuml_salt/renderer.rb +138 -0
- data/lib/etassia/notation/plantuml_salt/row.rb +19 -0
- data/lib/etassia/notation/plantuml_salt/run.rb +34 -0
- data/lib/etassia/notation/plantuml_salt/scene.rb +23 -0
- data/lib/etassia/notation/plantuml_salt/scroll_marks.rb +94 -0
- data/lib/etassia/notation/plantuml_salt/tab_marks.rb +74 -0
- data/lib/etassia/notation/plantuml_salt/tree_marks.rb +127 -0
- data/lib/etassia/notation/plantuml_salt/widget.rb +40 -0
- data/lib/etassia/notation/plantuml_salt/widget_marks.rb +319 -0
- data/lib/etassia/notation/plantuml_salt.rb +66 -0
- data/lib/etassia/notation/plugin_failure.rb +42 -0
- data/lib/etassia/notation.rb +289 -0
- data/lib/etassia/parser/architecture.rb +25 -0
- data/lib/etassia/parser/atoms/delimited_run.rb +82 -0
- data/lib/etassia/parser/atoms/greedy_run.rb +116 -0
- data/lib/etassia/parser/atoms/joined.rb +47 -0
- data/lib/etassia/parser/atoms/trimmed_run.rb +47 -0
- data/lib/etassia/parser/base.rb +258 -0
- data/lib/etassia/parser/block.rb +32 -0
- data/lib/etassia/parser/builders/architecture.rb +135 -0
- data/lib/etassia/parser/builders/block.rb +288 -0
- data/lib/etassia/parser/builders/c4.rb +275 -0
- data/lib/etassia/parser/builders/capture_string.rb +17 -0
- data/lib/etassia/parser/builders/class_diagram.rb +539 -0
- data/lib/etassia/parser/builders/er_diagram.rb +308 -0
- data/lib/etassia/parser/builders/error.rb +51 -0
- data/lib/etassia/parser/builders/flowchart.rb +1150 -0
- data/lib/etassia/parser/builders/gantt.rb +267 -0
- data/lib/etassia/parser/builders/git_graph.rb +301 -0
- data/lib/etassia/parser/builders/info.rb +57 -0
- data/lib/etassia/parser/builders/kanban.rb +411 -0
- data/lib/etassia/parser/builders/mindmap.rb +271 -0
- data/lib/etassia/parser/builders/packet.rb +85 -0
- data/lib/etassia/parser/builders/pie.rb +105 -0
- data/lib/etassia/parser/builders/quadrant.rb +165 -0
- data/lib/etassia/parser/builders/radar.rb +157 -0
- data/lib/etassia/parser/builders/requirement.rb +230 -0
- data/lib/etassia/parser/builders/sankey.rb +142 -0
- data/lib/etassia/parser/builders/sequence.rb +431 -0
- data/lib/etassia/parser/builders/sequence_blocks.rb +117 -0
- data/lib/etassia/parser/builders/sequence_box_title.rb +57 -0
- data/lib/etassia/parser/builders/state_diagram.rb +364 -0
- data/lib/etassia/parser/builders/timeline.rb +133 -0
- data/lib/etassia/parser/builders/treemap.rb +138 -0
- data/lib/etassia/parser/builders/xy_chart.rb +148 -0
- data/lib/etassia/parser/c4.rb +37 -0
- data/lib/etassia/parser/class_diagram.rb +58 -0
- data/lib/etassia/parser/class_namespaces.rb +62 -0
- data/lib/etassia/parser/class_notes.rb +54 -0
- data/lib/etassia/parser/er_diagram.rb +28 -0
- data/lib/etassia/parser/error.rb +30 -0
- data/lib/etassia/parser/flowchart.rb +87 -0
- data/lib/etassia/parser/gantt.rb +43 -0
- data/lib/etassia/parser/git_graph.rb +106 -0
- data/lib/etassia/parser/grammars/architecture.rb +165 -0
- data/lib/etassia/parser/grammars/block.rb +190 -0
- data/lib/etassia/parser/grammars/c4.rb +229 -0
- data/lib/etassia/parser/grammars/class_diagram.rb +606 -0
- data/lib/etassia/parser/grammars/common.rb +107 -0
- data/lib/etassia/parser/grammars/er_diagram.rb +232 -0
- data/lib/etassia/parser/grammars/error.rb +32 -0
- data/lib/etassia/parser/grammars/flowchart.rb +1722 -0
- data/lib/etassia/parser/grammars/gantt.rb +198 -0
- data/lib/etassia/parser/grammars/git_graph.rb +234 -0
- data/lib/etassia/parser/grammars/info.rb +53 -0
- data/lib/etassia/parser/grammars/kanban.rb +417 -0
- data/lib/etassia/parser/grammars/mermaid_unicode_text.rb +108 -0
- data/lib/etassia/parser/grammars/mindmap.rb +220 -0
- data/lib/etassia/parser/grammars/packet.rb +71 -0
- data/lib/etassia/parser/grammars/pie.rb +128 -0
- data/lib/etassia/parser/grammars/quadrant.rb +202 -0
- data/lib/etassia/parser/grammars/radar.rb +158 -0
- data/lib/etassia/parser/grammars/requirement.rb +258 -0
- data/lib/etassia/parser/grammars/sankey.rb +104 -0
- data/lib/etassia/parser/grammars/sequence.rb +940 -0
- data/lib/etassia/parser/grammars/state_diagram.rb +382 -0
- data/lib/etassia/parser/grammars/timeline.rb +153 -0
- data/lib/etassia/parser/grammars/treemap.rb +122 -0
- data/lib/etassia/parser/grammars/xy_chart.rb +119 -0
- data/lib/etassia/parser/info.rb +32 -0
- data/lib/etassia/parser/kanban.rb +131 -0
- data/lib/etassia/parser/mermaid_shapes.rb +106 -0
- data/lib/etassia/parser/metadata_yaml.rb +452 -0
- data/lib/etassia/parser/mindmap.rb +92 -0
- data/lib/etassia/parser/native/builders/architecture.rb +103 -0
- data/lib/etassia/parser/native/builders/block.rb +68 -0
- data/lib/etassia/parser/native/builders/c4.rb +16 -0
- data/lib/etassia/parser/native/builders/class_diagram.rb +63 -0
- data/lib/etassia/parser/native/builders/er_diagram.rb +47 -0
- data/lib/etassia/parser/native/builders/flowchart.rb +58 -0
- data/lib/etassia/parser/native/builders/gantt.rb +35 -0
- data/lib/etassia/parser/native/builders/gitgraph.rb +35 -0
- data/lib/etassia/parser/native/builders/info.rb +30 -0
- data/lib/etassia/parser/native/builders/kanban.rb +37 -0
- data/lib/etassia/parser/native/builders/mindmap.rb +26 -0
- data/lib/etassia/parser/native/builders/packet.rb +63 -0
- data/lib/etassia/parser/native/builders/pie.rb +82 -0
- data/lib/etassia/parser/native/builders/quadrant.rb +27 -0
- data/lib/etassia/parser/native/builders/radar.rb +84 -0
- data/lib/etassia/parser/native/builders/requirement.rb +25 -0
- data/lib/etassia/parser/native/builders/sankey.rb +18 -0
- data/lib/etassia/parser/native/builders/sequence.rb +48 -0
- data/lib/etassia/parser/native/builders/state_diagram.rb +61 -0
- data/lib/etassia/parser/native/builders/timeline.rb +46 -0
- data/lib/etassia/parser/native/builders/treemap.rb +60 -0
- data/lib/etassia/parser/native/builders/user_journey.rb +83 -0
- data/lib/etassia/parser/native/builders/xy_chart.rb +94 -0
- data/lib/etassia/parser/native/declined.rb +12 -0
- data/lib/etassia/parser/native/mermaid.json +1 -0
- data/lib/etassia/parser/native/text.rb +31 -0
- data/lib/etassia/parser/native.rb +99 -0
- data/lib/etassia/parser/packet.rb +56 -0
- data/lib/etassia/parser/pie.rb +32 -0
- data/lib/etassia/parser/quadrant.rb +77 -0
- data/lib/etassia/parser/radar.rb +99 -0
- data/lib/etassia/parser/requirement.rb +32 -0
- data/lib/etassia/parser/sankey.rb +47 -0
- data/lib/etassia/parser/sequence.rb +34 -0
- data/lib/etassia/parser/state_diagram.rb +29 -0
- data/lib/etassia/parser/timeline.rb +40 -0
- data/lib/etassia/parser/treemap.rb +25 -0
- data/lib/etassia/parser/user_journey.rb +253 -0
- data/lib/etassia/parser/xy_chart.rb +98 -0
- data/lib/etassia/parser.rb +18 -0
- data/lib/etassia/renderer/architecture.rb +232 -0
- data/lib/etassia/renderer/architecture_edge_router.rb +379 -0
- data/lib/etassia/renderer/base.rb +295 -0
- data/lib/etassia/renderer/block.rb +157 -0
- data/lib/etassia/renderer/c4.rb +294 -0
- data/lib/etassia/renderer/class_diagram.rb +671 -0
- data/lib/etassia/renderer/er_diagram.rb +842 -0
- data/lib/etassia/renderer/error.rb +131 -0
- data/lib/etassia/renderer/flowchart.rb +315 -0
- data/lib/etassia/renderer/gantt.rb +177 -0
- data/lib/etassia/renderer/git_graph.rb +224 -0
- data/lib/etassia/renderer/info.rb +59 -0
- data/lib/etassia/renderer/kanban.rb +157 -0
- data/lib/etassia/renderer/line_break_text.rb +56 -0
- data/lib/etassia/renderer/markdown_text.rb +106 -0
- data/lib/etassia/renderer/mindmap.rb +290 -0
- data/lib/etassia/renderer/packet.rb +87 -0
- data/lib/etassia/renderer/pie.rb +113 -0
- data/lib/etassia/renderer/quadrant.rb +189 -0
- data/lib/etassia/renderer/radar.rb +191 -0
- data/lib/etassia/renderer/requirement.rb +209 -0
- data/lib/etassia/renderer/sankey.rb +114 -0
- data/lib/etassia/renderer/sequence/frame_drawing.rb +169 -0
- data/lib/etassia/renderer/sequence/note_drawing.rb +69 -0
- data/lib/etassia/renderer/sequence.rb +490 -0
- data/lib/etassia/renderer/state_diagram.rb +202 -0
- data/lib/etassia/renderer/timeline.rb +134 -0
- data/lib/etassia/renderer/timeline_palette.rb +18 -0
- data/lib/etassia/renderer/treemap/section_total.rb +33 -0
- data/lib/etassia/renderer/treemap.rb +128 -0
- data/lib/etassia/renderer/user_journey.rb +209 -0
- data/lib/etassia/renderer/xy_chart.rb +180 -0
- data/lib/etassia/renderer.rb +22 -0
- data/lib/etassia/runtime.rb +125 -0
- data/lib/etassia/source/frontmatter.rb +501 -0
- data/lib/etassia/source.rb +224 -0
- data/lib/etassia/svg/arrowhead.rb +205 -0
- data/lib/etassia/svg/circle.rb +19 -0
- data/lib/etassia/svg/document.rb +106 -0
- data/lib/etassia/svg/element.rb +209 -0
- data/lib/etassia/svg/ellipse.rb +23 -0
- data/lib/etassia/svg/escaping.rb +149 -0
- data/lib/etassia/svg/group.rb +64 -0
- data/lib/etassia/svg/line.rb +21 -0
- data/lib/etassia/svg/numbers.rb +53 -0
- data/lib/etassia/svg/path.rb +67 -0
- data/lib/etassia/svg/path_geometry.rb +378 -0
- data/lib/etassia/svg/polygon.rb +25 -0
- data/lib/etassia/svg/polyline.rb +28 -0
- data/lib/etassia/svg/rect.rb +34 -0
- data/lib/etassia/svg/style.rb +50 -0
- data/lib/etassia/svg/text.rb +181 -0
- data/lib/etassia/svg/tspan.rb +48 -0
- data/lib/etassia/svg.rb +38 -0
- data/lib/etassia/text_measurement/arial_advances.rb +122 -0
- data/lib/etassia/text_measurement/dejavu_advances.rb +111 -0
- data/lib/etassia/text_measurement/times_advances.rb +64 -0
- data/lib/etassia/text_measurement/trebuchet_advances.rb +53 -0
- data/lib/etassia/text_measurement/trebuchet_kerning.rb +29 -0
- data/lib/etassia/text_measurement.rb +198 -0
- data/lib/etassia/theme/builtin/dark.yml +229 -0
- data/lib/etassia/theme/builtin/default.yml +239 -0
- data/lib/etassia/theme/builtin/high_contrast.yml +231 -0
- data/lib/etassia/theme/builtin/light.yml +229 -0
- data/lib/etassia/theme/color_palette.rb +97 -0
- data/lib/etassia/theme/completion.rb +50 -0
- data/lib/etassia/theme/effect_styles.rb +34 -0
- data/lib/etassia/theme/registry.rb +45 -0
- data/lib/etassia/theme/shape_styles.rb +34 -0
- data/lib/etassia/theme/spacing_config.rb +30 -0
- data/lib/etassia/theme/typography.rb +40 -0
- data/lib/etassia/theme.rb +73 -0
- data/lib/etassia/version.rb +5 -0
- data/lib/etassia.rb +40 -0
- metadata +865 -0
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "../layout/er_diagram"
|
|
5
|
+
|
|
6
|
+
module Etassia
|
|
7
|
+
module Renderer
|
|
8
|
+
# ER diagram renderer for converting graphs to SVG.
|
|
9
|
+
#
|
|
10
|
+
# Converts a laid-out graph structure (with computed positions) into
|
|
11
|
+
# SVG using the Svg builder classes. Handles ER entity boxes with
|
|
12
|
+
# attributes, PK/FK markers, and relationship lines with crow's foot
|
|
13
|
+
# cardinality notation.
|
|
14
|
+
#
|
|
15
|
+
# @example Render an ER diagram
|
|
16
|
+
# renderer = ErDiagram.new
|
|
17
|
+
# svg = renderer.render(laid_out_graph)
|
|
18
|
+
class ErDiagram < Base
|
|
19
|
+
# Renders final ER geometry to SVG. Hash input remains accepted as a
|
|
20
|
+
# compatibility boundary and is converted before rendering.
|
|
21
|
+
#
|
|
22
|
+
# @param scene [Layout::ErDiagram::Scene, Hash]
|
|
23
|
+
# @return [Svg::Document] the rendered SVG document
|
|
24
|
+
def render(scene)
|
|
25
|
+
return render_graph(scene) if scene.is_a?(Hash)
|
|
26
|
+
|
|
27
|
+
svg = create_document(scene)
|
|
28
|
+
render_relationships(scene, svg)
|
|
29
|
+
render_entities(scene, svg)
|
|
30
|
+
|
|
31
|
+
svg
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
protected
|
|
35
|
+
|
|
36
|
+
def render_graph(graph)
|
|
37
|
+
padding = Layout::ErDiagram.diagram_padding(graph) * 2
|
|
38
|
+
scene = Layout::ErDiagram::Scene.new(
|
|
39
|
+
width: calculate_width(graph) + padding,
|
|
40
|
+
height: calculate_height(graph) + padding,
|
|
41
|
+
)
|
|
42
|
+
svg = create_document(scene)
|
|
43
|
+
render_relationships(graph, svg) if graph[:edges]
|
|
44
|
+
render_entities(graph, svg) if graph[:children]
|
|
45
|
+
svg
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def emit_entities(nodes, class_defs, svg)
|
|
49
|
+
nodes.each { |node| emit_entity(node, class_defs, svg) }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def emit_entity(node, class_defs, svg)
|
|
53
|
+
styles = entity_styles(node, class_defs)
|
|
54
|
+
group = Svg::Group.new.tap { |item| item.id = "entity-#{node.id}" }
|
|
55
|
+
group.children << entity_box(node, styles)
|
|
56
|
+
emit_entity_content(node, group, styles)
|
|
57
|
+
svg << group
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def entity_box(node, styles)
|
|
61
|
+
attributed = node.attributes.any?
|
|
62
|
+
rect = Svg::Rect.new
|
|
63
|
+
apply_entity_geometry(rect, node)
|
|
64
|
+
apply_entity_styles(rect, styles, attributed)
|
|
65
|
+
rect
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def apply_entity_geometry(rect, node)
|
|
69
|
+
rect.x = node.x
|
|
70
|
+
rect.y = node.y
|
|
71
|
+
rect.width = node.width
|
|
72
|
+
rect.height = node.height
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def apply_entity_styles(rect, styles, attributed)
|
|
76
|
+
rect.fill = box_property(styles, "fill", attributed) ||
|
|
77
|
+
theme_color(:node_fill)
|
|
78
|
+
rect.stroke = entity_stroke(styles, attributed)
|
|
79
|
+
rect.stroke_width =
|
|
80
|
+
box_property(styles, "stroke-width", attributed) || "2"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def entity_stroke(styles, attributed)
|
|
84
|
+
box_property(styles, "stroke", attributed) ||
|
|
85
|
+
theme_color(:node_stroke)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def emit_entity_content(node, group, styles)
|
|
89
|
+
emit_entity_header(node, group, styles)
|
|
90
|
+
node.attributes.each do |attribute|
|
|
91
|
+
emit_attribute(attribute, styles, group)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def emit_entity_header(node, group, styles)
|
|
96
|
+
group.children << entity_header_text(node.labels.first, styles)
|
|
97
|
+
group.children << entity_separator(
|
|
98
|
+
node.separator, styles, node.attributes.any?
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def entity_header_text(label, styles)
|
|
103
|
+
Svg::Text.new(
|
|
104
|
+
x: label.x,
|
|
105
|
+
y: label.y,
|
|
106
|
+
content: label.text,
|
|
107
|
+
fill: styles["color"] || theme_color(:label_text),
|
|
108
|
+
font_family: "Arial, sans-serif",
|
|
109
|
+
font_size: font_size_value(label.font_size),
|
|
110
|
+
text_anchor: "middle",
|
|
111
|
+
font_weight: "bold",
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def entity_separator(geometry, styles, attributed)
|
|
116
|
+
Svg::Line.new.tap do |line|
|
|
117
|
+
line.x1 = geometry.x1
|
|
118
|
+
line.y1 = geometry.y1
|
|
119
|
+
line.x2 = geometry.x2
|
|
120
|
+
line.y2 = geometry.y2
|
|
121
|
+
line.stroke = entity_stroke(styles, attributed)
|
|
122
|
+
line.stroke_width = "1"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def emit_attribute(attribute, styles, group)
|
|
127
|
+
text = Svg::Text.new(
|
|
128
|
+
x: attribute.x,
|
|
129
|
+
y: attribute.y,
|
|
130
|
+
content: attribute.text,
|
|
131
|
+
fill: styles["color"] || theme_color(:label_text),
|
|
132
|
+
font_family: "monospace",
|
|
133
|
+
font_size: font_size_value(attribute.font_size),
|
|
134
|
+
)
|
|
135
|
+
group.children << text
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Every entity carries an implicit `default` class ahead of whatever
|
|
139
|
+
# it was explicitly assigned — verified against mermaid's own db,
|
|
140
|
+
# whose `cssClasses` opens with the literal string "default" for
|
|
141
|
+
# every entity, assigned or not (`"default"`, `"default a"`,
|
|
142
|
+
# `"default a b a"`). A `classDef default` is stored like any other
|
|
143
|
+
# name (grammar and process_class_def need no change for this); this
|
|
144
|
+
# is the one place it has to be applied even when nothing assigned it.
|
|
145
|
+
DEFAULT_CLASS = "default"
|
|
146
|
+
private_constant :DEFAULT_CLASS
|
|
147
|
+
|
|
148
|
+
# Resolves the style properties an entity's classes apply, by name and
|
|
149
|
+
# not by position — an index-keyed lookup would silently swap two
|
|
150
|
+
# entities' colours the moment their class lists diverge in length.
|
|
151
|
+
# Classes merge left to right: a later class overrides an earlier one
|
|
152
|
+
# only on a property both declare, and each contributes any property
|
|
153
|
+
# the other did not. NOT deduped upstream, so a repeated assignment
|
|
154
|
+
# moves that class to the end and lets it win a later conflict —
|
|
155
|
+
# verified against mermaid: `CAR:::a,b` then `CAR:::a` resolves
|
|
156
|
+
# `fill:red` (the trailing "a"), not `fill:blue`.
|
|
157
|
+
#
|
|
158
|
+
# @param node [Hash] the graph node, holding metadata[:classes]
|
|
159
|
+
# @param class_defs [Hash{String => String}] declared classDef styles
|
|
160
|
+
# @return [Hash{String => String}] resolved property => value
|
|
161
|
+
# Classes merge left to right into ONE Hash, not per-class then
|
|
162
|
+
# `Hash#merge!` — mermaid resolves every class's declarations into a
|
|
163
|
+
# single Map at the very end (`styles2Map` in its own bundle), so a
|
|
164
|
+
# chunk from an earlier class and a same-exact-case chunk from a later
|
|
165
|
+
# class must update the SAME Map slot in the SAME left-to-right pass.
|
|
166
|
+
# Plain assignment into one accumulator reproduces that: a repeated
|
|
167
|
+
# exact key updates its value in place (keeping its first position,
|
|
168
|
+
# same as `Map#set` on an existing key) and a new key is appended.
|
|
169
|
+
def entity_styles(node, class_defs)
|
|
170
|
+
classes = [DEFAULT_CLASS, *node.classes]
|
|
171
|
+
|
|
172
|
+
classes.each_with_object({}) do |class_name, styles|
|
|
173
|
+
declaration = class_defs.find { |item| item.name == class_name }
|
|
174
|
+
next unless declaration
|
|
175
|
+
|
|
176
|
+
apply_chunks(class_chunks(declaration.declaration), styles)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Splits a `classDef` style run ("fill:#f96,stroke:#333") into its raw
|
|
181
|
+
# comma-separated chunks, then REPLAYS a copy of every chunk whose
|
|
182
|
+
# text contains the lowercase substring "color" — anywhere, key or
|
|
183
|
+
# value — onto the end of the list, EXCEPT one declaring `fill`.
|
|
184
|
+
#
|
|
185
|
+
# Verified against mermaid's own db (`ErDB#addClass`): each style
|
|
186
|
+
# chunk is tested with `/color/.exec(s)` (no `i` flag, so only a
|
|
187
|
+
# literal lowercase "color" substring matches) and, on a match, pushed
|
|
188
|
+
# a SECOND time into a separate `textStyles` array that gets appended
|
|
189
|
+
# after this class's own `styles` array when compiling. A declaration
|
|
190
|
+
# naming `stroke:currentcolor` this way ends up LAST among same-key
|
|
191
|
+
# entries even when a later, unrelated `stroke:red` chunk follows it
|
|
192
|
+
# in source — `classDef a stroke:currentcolor,stroke:blue`
|
|
193
|
+
# resolves to `currentcolor`, not `blue`, because the replay
|
|
194
|
+
# lands after both.
|
|
195
|
+
#
|
|
196
|
+
# `textStyles` is applied after this class's own styles, so a replayed
|
|
197
|
+
# chunk lands last among same-key entries. mermaid defuses that for
|
|
198
|
+
# `fill` by RENAMING it: the chunk pushed into `textStyles` becomes
|
|
199
|
+
# `bgFill:...`, which no longer collides with the box's `fill`.
|
|
200
|
+
#
|
|
201
|
+
# That rename is CASE-SENSITIVE, which is why the exemption below is
|
|
202
|
+
# too. Executing mermaid's own db API for these declarations returns
|
|
203
|
+
#
|
|
204
|
+
# textStyles: ["bgFill:currentcolor", "FILL:currentcolor",
|
|
205
|
+
# "stroke:currentcolor"]
|
|
206
|
+
#
|
|
207
|
+
# — lowercase `fill` rewritten, `FILL` left alone to collide as before.
|
|
208
|
+
# Folding the key here would exempt `FILL` as well and change a
|
|
209
|
+
# behaviour mermaid does not change.
|
|
210
|
+
#
|
|
211
|
+
# Measured with mmdc on an erDiagram entity carrying an attribute
|
|
212
|
+
# block, one class assigned:
|
|
213
|
+
#
|
|
214
|
+
# stroke:currentcolor,stroke:blue -> currentcolor
|
|
215
|
+
# fill:currentcolor,fill:blue -> blue
|
|
216
|
+
# stroke:green,stroke:blue -> blue
|
|
217
|
+
# fill:green,fill:blue -> blue
|
|
218
|
+
#
|
|
219
|
+
# Row one is why the replay exists. Row two is why lowercase `fill`
|
|
220
|
+
# is exempt: replaying it reverses that row to `currentcolor`.
|
|
221
|
+
#
|
|
222
|
+
# @param declaration [String] raw style text for one class
|
|
223
|
+
# @return [Array<String>] chunks, with colour-bearing ones repeated
|
|
224
|
+
def class_chunks(declaration)
|
|
225
|
+
chunks = declaration.split(",")
|
|
226
|
+
chunks + chunks.select { |chunk| replayed_chunk?(chunk) }
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Whether CHUNK is one whose `textStyles` replay is still visible on
|
|
230
|
+
# the entity BOX. See `class_chunks` for the measurement behind the
|
|
231
|
+
# `fill` exemption.
|
|
232
|
+
#
|
|
233
|
+
# The key is compared EXACTLY, not folded. mermaid's rename of `fill`
|
|
234
|
+
# to `bgFill` is case-sensitive, so `FILL:` is still replayed and
|
|
235
|
+
# still collides. Folding here would exempt it too, and change a
|
|
236
|
+
# behaviour this commit has no business changing.
|
|
237
|
+
#
|
|
238
|
+
# @param chunk [String] one raw comma-separated chunk
|
|
239
|
+
# @return [Boolean]
|
|
240
|
+
def replayed_chunk?(chunk)
|
|
241
|
+
return false unless chunk.include?("color")
|
|
242
|
+
|
|
243
|
+
key, value = mermaid_split(chunk)
|
|
244
|
+
!value.nil? && key.strip != "fill"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Applies a run of raw chunks into the shared, exact-case styles Hash,
|
|
248
|
+
# left to right. A chunk with no colon is skipped rather than raising
|
|
249
|
+
# — `classDef x foo` and `classDef x font-family:Arial,sans-serif`
|
|
250
|
+
# both parse under mermaid, and the second is exactly this shape after
|
|
251
|
+
# the comma split, so failing here would crash a render on
|
|
252
|
+
# mermaid-valid input.
|
|
253
|
+
#
|
|
254
|
+
# Neither side is folded. Verified against mermaid's own db: it stores
|
|
255
|
+
# a style declaration VERBATIM, case untouched ("FILL:red" stays
|
|
256
|
+
# "FILL:red"). Case-insensitive matching is the BROWSER's doing, on
|
|
257
|
+
# the finished CSS text, not mermaid's — so folding here, before that
|
|
258
|
+
# point, changes results mermaid never produces:
|
|
259
|
+
#
|
|
260
|
+
# - `fill:red,FILL:blue,fill:green` must compute blue in Chrome, not
|
|
261
|
+
# green. Downcasing collapses all three into one Ruby key up front,
|
|
262
|
+
# which loses the exact-case Hash entries mermaid's own Map keeps
|
|
263
|
+
# ("fill" and "FILL" are different keys to it too) and makes the
|
|
264
|
+
# LAST literal chunk win regardless of case — wrong order. Plain
|
|
265
|
+
# assignment into one accumulator, above, already reproduces the
|
|
266
|
+
# ordering a case-preserving Map gives mermaid; box_style below is
|
|
267
|
+
# what does the browser's case-insensitive resolution, once, at
|
|
268
|
+
# lookup.
|
|
269
|
+
# - `COLOR:red` (uppercase) must be a BOX style, not a label one —
|
|
270
|
+
# mermaid's own isLabelStyle check (`key === "color"`,
|
|
271
|
+
# handDrawnShapeStyles.ts) is exact-case, so `COLOR` fails it.
|
|
272
|
+
# Downcasing here would fold it into the same key as a real
|
|
273
|
+
# lowercase `color:`, and a caller reading text color
|
|
274
|
+
# (`styles['color']`) would pick it up and colour the wrong
|
|
275
|
+
# element.
|
|
276
|
+
#
|
|
277
|
+
# @param chunks [Array<String>] raw "key:value" chunks, in order
|
|
278
|
+
# @param styles [Hash{String => String}] accumulator, mutated in place
|
|
279
|
+
# @return [Hash{String => String}] the same accumulator, for chaining
|
|
280
|
+
def apply_chunks(chunks, styles)
|
|
281
|
+
chunks.each do |chunk|
|
|
282
|
+
key, value = mermaid_split(chunk)
|
|
283
|
+
next unless value
|
|
284
|
+
|
|
285
|
+
styles[key.strip] = value.strip
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Splits one "key:value" chunk the way mermaid's own `styles2Map`
|
|
290
|
+
# does: `style.split(":")` with NO limit, destructured to only the
|
|
291
|
+
# first two elements. A third colon-separated segment is silently
|
|
292
|
+
# DROPPED, not folded into the value — `fill:red:blue` resolves to
|
|
293
|
+
# `red`, and Chrome computes red from mermaid's own output for that
|
|
294
|
+
# source. `String#split(':', 2)` (the previous behaviour here) instead
|
|
295
|
+
# keeps everything after the first colon, giving `red:blue`, which
|
|
296
|
+
# mermaid never produces.
|
|
297
|
+
#
|
|
298
|
+
# `-1` is required to match JavaScript's `split`: Ruby's default
|
|
299
|
+
# `split` drops a TRAILING empty field, so `"fill:".split(':')` gives
|
|
300
|
+
# `["fill"]` and a value-clearing declaration reads as "no colon" and
|
|
301
|
+
# is skipped, leaving a stale earlier `fill:red` in place. JavaScript
|
|
302
|
+
# (and this, with `-1`) both give `["fill", ""]` instead — `present`
|
|
303
|
+
# below is what then treats that resolved empty value as "never
|
|
304
|
+
# validly declared" once a caller reads it back, matching a browser
|
|
305
|
+
# dropping an empty CSS declaration and mermaid's own JS-falsy read.
|
|
306
|
+
#
|
|
307
|
+
# @param chunk [String] one raw "key:value(:ignored)" chunk
|
|
308
|
+
# @return [Array(String, String), Array(String, nil), Array()]
|
|
309
|
+
# [key, value] — value is nil when the chunk has no colon, and both
|
|
310
|
+
# are nil (empty array) for an empty chunk
|
|
311
|
+
def mermaid_split(chunk)
|
|
312
|
+
parts = chunk.split(":", -1)
|
|
313
|
+
[parts[0], parts[1]]
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# An empty string is a validly-parsed but empty CSS value — mermaid's
|
|
317
|
+
# own override read is JS-falsy on it, and the browser drops an empty
|
|
318
|
+
# declaration (`fill:`) during cascade — so both treat it as though
|
|
319
|
+
# the property was never declared. Applied to the exact-key
|
|
320
|
+
# (attributed) read below; the case-insensitive `box_style` path
|
|
321
|
+
# below reaches the same result through `css_valid?` instead, since
|
|
322
|
+
# an empty string is not valid CSS for any property this file models.
|
|
323
|
+
#
|
|
324
|
+
# @param value [String, nil]
|
|
325
|
+
# @return [String, nil] VALUE, or nil if it is nil or empty
|
|
326
|
+
def present(value)
|
|
327
|
+
value.nil? || value.empty? ? nil : value
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Picks the entry nearest the end of VALUES whose value passes the
|
|
331
|
+
# block, treating an explicitly-cleared empty value (see `present`)
|
|
332
|
+
# as absent rather than invalid. When nothing validates, falls back
|
|
333
|
+
# to the raw last-declared value instead of nil — a hostile or
|
|
334
|
+
# malformed value must still reach the rendered SVG verbatim
|
|
335
|
+
# (escaped by the XML serializer, not by us silently discarding it
|
|
336
|
+
# for our own default), which is exactly the guarantee the D2
|
|
337
|
+
# integration spec pins. Only used where that guarantee applies —
|
|
338
|
+
# see `ambient_color` below for the one caller that must NOT fall
|
|
339
|
+
# back this way.
|
|
340
|
+
#
|
|
341
|
+
# @param values [Array<String, nil>] raw candidate values, in
|
|
342
|
+
# source order
|
|
343
|
+
# @yield [String] validity predicate
|
|
344
|
+
# @return [String, nil] the winning value, or nil if VALUES held
|
|
345
|
+
# nothing but absent (nil or empty) entries
|
|
346
|
+
def last_valid_or_raw(values, &)
|
|
347
|
+
present_values = values.filter_map { |value| present(value) }
|
|
348
|
+
return nil if present_values.empty?
|
|
349
|
+
|
|
350
|
+
present_values.reverse_each.find(&) || present_values.last
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Case-insensitive box lookup for a BARE entity, modeling the
|
|
354
|
+
# browser's cascade on the box's `style=` attribute: among every
|
|
355
|
+
# entry whose key matches PROPERTY case-insensitively, in source
|
|
356
|
+
# order, the value nearest the end wins — same as the last matching
|
|
357
|
+
# CSS declaration in a rendered `style` attribute — but an entry
|
|
358
|
+
# whose value the browser's CSS parser would reject is skipped,
|
|
359
|
+
# exactly as an invalid declaration never reaches the cascade at
|
|
360
|
+
# all. Do not use this for `color` — see `apply_chunks`'s second
|
|
361
|
+
# bullet.
|
|
362
|
+
#
|
|
363
|
+
# @param styles [Hash{String => String}] resolved, exact-case entity
|
|
364
|
+
# styles
|
|
365
|
+
# @param property [String] lowercase property name to resolve
|
|
366
|
+
# @return [String, nil] the winning value, or nil if PROPERTY was
|
|
367
|
+
# never declared under any case
|
|
368
|
+
def box_style(styles, property)
|
|
369
|
+
values = styles.select { |key, _| key.downcase == property }.values
|
|
370
|
+
last_valid_or_raw(values) { |value| css_valid?(property, value) }
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# `currentColor` on a bare entity's box resolves, in a real browser,
|
|
374
|
+
# against that SAME element's `color` CSS property — which mermaid
|
|
375
|
+
# routes onto the box's `style=` attribute under any casing EXCEPT a
|
|
376
|
+
# literal lowercase `color` key (that one is a label style, see the
|
|
377
|
+
# exact-case `styles['color']` reads above, and never reaches the
|
|
378
|
+
# box's style attribute at all). Substitute the concrete value now,
|
|
379
|
+
# since a standalone SVG has no browser cascade left to resolve it
|
|
380
|
+
# at paint time.
|
|
381
|
+
#
|
|
382
|
+
# @param value [String, nil] PROPERTY's already-resolved raw value
|
|
383
|
+
# @param styles [Hash{String => String}] resolved, exact-case entity
|
|
384
|
+
# styles
|
|
385
|
+
# @return [String, nil] VALUE, or the ambient colour declared for
|
|
386
|
+
# this box if VALUE is `currentColor` (any case) and one exists
|
|
387
|
+
def resolve_current_color(value, styles)
|
|
388
|
+
return value unless value&.downcase == "currentcolor"
|
|
389
|
+
|
|
390
|
+
ambient_color(styles) || value
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# The ambient colour a bare entity's box would compute `color` as:
|
|
394
|
+
# every key matching `color` case-insensitively EXCEPT the literal
|
|
395
|
+
# lowercase key, which is the label style excluded from the box's
|
|
396
|
+
# own `style=` attribute — see `resolve_current_color` above. Unlike
|
|
397
|
+
# `box_style`, an invalid ambient declaration is simply absent
|
|
398
|
+
# rather than falling back to its raw text — `resolve_current_color`
|
|
399
|
+
# already has a safe literal default (leave `currentColor` as
|
|
400
|
+
# written, which a standalone browser resolves to black, matching
|
|
401
|
+
# mermaid with no ambient colour set), so there is no verbatim
|
|
402
|
+
# guarantee to uphold here the way there is for the fill/stroke
|
|
403
|
+
# attribute itself.
|
|
404
|
+
#
|
|
405
|
+
# @param styles [Hash{String => String}] resolved, exact-case entity
|
|
406
|
+
# styles
|
|
407
|
+
# @return [String, nil] the winning ambient colour, or nil if none
|
|
408
|
+
# was validly declared
|
|
409
|
+
def ambient_color(styles)
|
|
410
|
+
values = styles.select do |key, _value|
|
|
411
|
+
key != "color" && key.downcase == "color"
|
|
412
|
+
end.values
|
|
413
|
+
values.filter_map { |value| present(value) }
|
|
414
|
+
.reverse_each.find { |value| css_color?(value) }
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Colour-valued box properties resolve `currentColor` against the
|
|
418
|
+
# box's ambient colour (see `resolve_current_color`); length-valued
|
|
419
|
+
# ones do not.
|
|
420
|
+
COLOR_PROPERTIES = %w[fill stroke].freeze
|
|
421
|
+
private_constant :COLOR_PROPERTIES
|
|
422
|
+
|
|
423
|
+
# Whether VALUE is syntactically valid CSS for PROPERTY. A small
|
|
424
|
+
# table, not a CSS grammar — enough to tell a real declaration from
|
|
425
|
+
# `FILL:bogus`, not to validate every legal `rgb()` argument.
|
|
426
|
+
# `box_property` only asks about `fill`, `stroke` and `stroke-width`.
|
|
427
|
+
#
|
|
428
|
+
# @param property [String] "fill", "stroke" or "stroke-width"
|
|
429
|
+
# @param value [String]
|
|
430
|
+
# @return [Boolean]
|
|
431
|
+
def css_valid?(property, value)
|
|
432
|
+
return css_length?(value) if property == "stroke-width"
|
|
433
|
+
|
|
434
|
+
css_color?(value)
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
CSS_HEX_COLOR = /\A#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})\z/i
|
|
438
|
+
private_constant :CSS_HEX_COLOR
|
|
439
|
+
|
|
440
|
+
CSS_COLOR_FUNCTION = /\A(?:rgb|rgba|hsl|hsla)\(.+\)\z/i
|
|
441
|
+
private_constant :CSS_COLOR_FUNCTION
|
|
442
|
+
|
|
443
|
+
CSS_LENGTH = %r{
|
|
444
|
+
\A\d+(?:\.\d+)?(?:px|em|rem|%|pt|cm|mm|in|pc|ex|ch|vw|vh)?\z
|
|
445
|
+
}ix
|
|
446
|
+
private_constant :CSS_LENGTH
|
|
447
|
+
|
|
448
|
+
# The CSS Color Module Level 4 extended keyword set, plus `none` (a
|
|
449
|
+
# valid `fill`/`stroke` paint keyword, not a general colour) and the
|
|
450
|
+
# two keywords that resolve dynamically (`currentcolor`) or to
|
|
451
|
+
# nothing (`transparent`). Not exhaustive of every legal CSS
|
|
452
|
+
# <color> (system colours, `color(...)`, `lab()` and friends are
|
|
453
|
+
# not corpus-observed here) — sufficient to separate a real
|
|
454
|
+
# declaration from `bogus`.
|
|
455
|
+
CSS_COLOR_KEYWORDS = %w[
|
|
456
|
+
aliceblue antiquewhite aqua aquamarine azure beige bisque black
|
|
457
|
+
blanchedalmond blue blueviolet brown burlywood cadetblue
|
|
458
|
+
chartreuse chocolate coral cornflowerblue cornsilk crimson cyan
|
|
459
|
+
currentcolor darkblue darkcyan darkgoldenrod darkgray darkgreen
|
|
460
|
+
darkgrey darkkhaki darkmagenta darkolivegreen darkorange
|
|
461
|
+
darkorchid darkred darksalmon darkseagreen darkslateblue
|
|
462
|
+
darkslategray darkslategrey darkturquoise darkviolet deeppink
|
|
463
|
+
deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite
|
|
464
|
+
forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray
|
|
465
|
+
green greenyellow grey honeydew hotpink indianred indigo ivory
|
|
466
|
+
khaki lavender lavenderblush lawngreen lemonchiffon lightblue
|
|
467
|
+
lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen
|
|
468
|
+
lightgrey lightpink lightsalmon lightseagreen lightskyblue
|
|
469
|
+
lightslategray lightslategrey lightsteelblue lightyellow lime
|
|
470
|
+
limegreen linen magenta maroon mediumaquamarine mediumblue
|
|
471
|
+
mediumorchid mediumpurple mediumseagreen mediumslateblue
|
|
472
|
+
mediumspringgreen mediumturquoise mediumvioletred midnightblue
|
|
473
|
+
mintcream mistyrose moccasin navajowhite navy none oldlace olive
|
|
474
|
+
olivedrab orange orangered orchid palegoldenrod palegreen
|
|
475
|
+
paleturquoise palevioletred papayawhip peachpuff peru pink plum
|
|
476
|
+
powderblue purple rebeccapurple red rosybrown royalblue
|
|
477
|
+
saddlebrown salmon sandybrown seagreen seashell sienna silver
|
|
478
|
+
skyblue slateblue slategray slategrey snow springgreen steelblue
|
|
479
|
+
tan teal thistle tomato transparent turquoise violet wheat white
|
|
480
|
+
whitesmoke yellow yellowgreen
|
|
481
|
+
].freeze
|
|
482
|
+
private_constant :CSS_COLOR_KEYWORDS
|
|
483
|
+
|
|
484
|
+
# @param value [String]
|
|
485
|
+
# @return [Boolean] whether VALUE is a CSS colour (or the `none`
|
|
486
|
+
# paint keyword) mermaid's browser cascade would accept, rather
|
|
487
|
+
# than dropping the declaration
|
|
488
|
+
def css_color?(value)
|
|
489
|
+
downcased = value.downcase
|
|
490
|
+
CSS_COLOR_KEYWORDS.include?(downcased) ||
|
|
491
|
+
CSS_HEX_COLOR.match?(value) ||
|
|
492
|
+
CSS_COLOR_FUNCTION.match?(value)
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# @param value [String]
|
|
496
|
+
# @return [Boolean] whether VALUE is a CSS <length> `stroke-width`
|
|
497
|
+
# would accept
|
|
498
|
+
def css_length?(value)
|
|
499
|
+
CSS_LENGTH.match?(value)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# An attributed entity's outer box does NOT go through the browser's
|
|
503
|
+
# CSS cascade the way a bare entity's does — mermaid draws it via a
|
|
504
|
+
# different, row-based shape whose fill, stroke, AND stroke-width
|
|
505
|
+
# each come from a direct, EXACT-key Map read (`stylesMap.get(...)`,
|
|
506
|
+
# mermaid's own `userNodeOverrides`) rather than an inline
|
|
507
|
+
# `style="..."` attribute the browser resolves case-insensitively.
|
|
508
|
+
# Verified by Codex against the installed mermaid 11.16.1 bundle and
|
|
509
|
+
# a real Chrome computation, for all three properties:
|
|
510
|
+
# `classDef a fill:red,FILL:blue,fill:green` on a bare CAR computes
|
|
511
|
+
# blue (what `box_style` gives), but on `CAR:::a { string make }` it
|
|
512
|
+
# computes green — the last literal lowercase `fill`, ignoring
|
|
513
|
+
# `FILL` entirely. `stroke:red,STROKE:blue,stroke:green,
|
|
514
|
+
# stroke-width:2px,STROKE-WIDTH:8px,stroke-width:4px` computes
|
|
515
|
+
# green/4px attributed, blue/8px bare — the same split, on both
|
|
516
|
+
# properties.
|
|
517
|
+
#
|
|
518
|
+
# `currentColor` is never resolved against an ambient colour on this
|
|
519
|
+
# path — an attributed box carries no `style=` attribute for the
|
|
520
|
+
# browser to cascade a `color` property through, so it has no
|
|
521
|
+
# ambient colour to read under any case, and the exact-key value is
|
|
522
|
+
# used verbatim.
|
|
523
|
+
#
|
|
524
|
+
# @param styles [Hash{String => String}] resolved, exact-case entity
|
|
525
|
+
# styles
|
|
526
|
+
# @param property [String] lowercase property name to resolve
|
|
527
|
+
# @param attributed [Boolean] whether this entity has any attributes
|
|
528
|
+
# @return [String, nil] the winning value, or nil if never validly
|
|
529
|
+
# declared under the case this path reads
|
|
530
|
+
def box_property(styles, property, attributed)
|
|
531
|
+
if attributed
|
|
532
|
+
present(styles[property])
|
|
533
|
+
else
|
|
534
|
+
value = box_style(styles, property)
|
|
535
|
+
if COLOR_PROPERTIES.include?(property)
|
|
536
|
+
resolve_current_color(value, styles)
|
|
537
|
+
else
|
|
538
|
+
value
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def emit_relationships(edges, svg)
|
|
544
|
+
edges.each { |edge| emit_relationship(edge, svg) }
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def emit_relationship(edge, svg)
|
|
548
|
+
group = Svg::Group.new.tap { |item| item.id = "rel-#{edge.id}" }
|
|
549
|
+
group.children.concat(relationship_shapes(edge))
|
|
550
|
+
emit_marker(edge.source_marker, group)
|
|
551
|
+
emit_marker(edge.target_marker, group)
|
|
552
|
+
emit_relationship_label(edge, group)
|
|
553
|
+
svg << group
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def relationship_shapes(edge)
|
|
557
|
+
edge.sections.map do |section|
|
|
558
|
+
relationship_shape(section, edge.relationship_type)
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def emit_relationship_label(edge, group)
|
|
563
|
+
return if edge.labels.empty?
|
|
564
|
+
|
|
565
|
+
group.children << relationship_text(edge.labels.first)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def relationship_shape(section, relationship_type)
|
|
569
|
+
return relationship_line(section, relationship_type) if
|
|
570
|
+
section.bend_points.empty?
|
|
571
|
+
|
|
572
|
+
relationship_path(section, relationship_type)
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def relationship_line(section, relationship_type)
|
|
576
|
+
Svg::Line.new.tap do |line|
|
|
577
|
+
apply_line_geometry(line, section)
|
|
578
|
+
relationship_stroke(line, relationship_type)
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
def apply_line_geometry(line, section)
|
|
583
|
+
line.x1 = section.start_point.x
|
|
584
|
+
line.y1 = section.start_point.y
|
|
585
|
+
line.x2 = section.end_point.x
|
|
586
|
+
line.y2 = section.end_point.y
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def relationship_path(section, relationship_type)
|
|
590
|
+
Svg::Path.new.tap do |path|
|
|
591
|
+
path.d = relationship_path_data(section)
|
|
592
|
+
path.fill = "none"
|
|
593
|
+
relationship_stroke(path, relationship_type)
|
|
594
|
+
end
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def relationship_path_data(section)
|
|
598
|
+
points = [section.start_point, *section.bend_points, section.end_point]
|
|
599
|
+
first = points.shift
|
|
600
|
+
(["M #{point_pair(first)}"] +
|
|
601
|
+
points.map { |point| "L #{point_pair(point)}" }).join(" ")
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
def point_pair(point)
|
|
605
|
+
"#{point.x} #{point.y}"
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def relationship_stroke(shape, relationship_type)
|
|
609
|
+
shape.stroke = theme_color(:edge_stroke)
|
|
610
|
+
shape.stroke_width = "2"
|
|
611
|
+
if relationship_type == "non-identifying"
|
|
612
|
+
shape.stroke_dasharray = "5,5"
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
def emit_marker(marker, group)
|
|
617
|
+
circles = marker.circles.map { |geometry| marker_circle(geometry) }
|
|
618
|
+
lines = marker.lines.map { |geometry| marker_line(geometry) }
|
|
619
|
+
children = marker.circle_first ? circles + lines : lines + circles
|
|
620
|
+
group.children.concat(children)
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def marker_line(geometry)
|
|
624
|
+
Svg::Line.new.tap do |line|
|
|
625
|
+
line.x1 = geometry.x1
|
|
626
|
+
line.y1 = geometry.y1
|
|
627
|
+
line.x2 = geometry.x2
|
|
628
|
+
line.y2 = geometry.y2
|
|
629
|
+
line.stroke = theme_color(:edge_stroke)
|
|
630
|
+
line.stroke_width = "2"
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
def marker_circle(geometry)
|
|
635
|
+
Svg::Circle.new.tap do |circle|
|
|
636
|
+
circle.cx = geometry.cx
|
|
637
|
+
circle.cy = geometry.cy
|
|
638
|
+
circle.r = geometry.radius
|
|
639
|
+
circle.fill = "none"
|
|
640
|
+
circle.stroke = theme_color(:edge_stroke)
|
|
641
|
+
circle.stroke_width = "2"
|
|
642
|
+
end
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
def relationship_text(label)
|
|
646
|
+
Svg::Text.new(
|
|
647
|
+
x: label.x,
|
|
648
|
+
y: label.y,
|
|
649
|
+
content: label.text,
|
|
650
|
+
fill: theme_color(:label_text),
|
|
651
|
+
font_family: "Arial, sans-serif",
|
|
652
|
+
font_size: font_size_value(label.font_size),
|
|
653
|
+
text_anchor: "middle",
|
|
654
|
+
)
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def font_size_value(font_size)
|
|
658
|
+
font_size.to_i == font_size ? font_size.to_i.to_s : font_size.to_s
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
# Released protected hooks. Hash compatibility delegates geometry
|
|
662
|
+
# recovery to Layout; typed scenes already carry final geometry.
|
|
663
|
+
def calculate_width(graph)
|
|
664
|
+
Layout::ErDiagram.content_width(graph)
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
def calculate_height(graph)
|
|
668
|
+
Layout::ErDiagram.content_height(graph)
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
def render_entities(graph, svg)
|
|
672
|
+
if graph.is_a?(Layout::ErDiagram::Scene)
|
|
673
|
+
return emit_entities(graph.children, graph.class_defs, svg)
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
render_graph_entities(graph, svg)
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
def render_graph_entities(graph, svg)
|
|
680
|
+
previous = @compatibility_class_defs
|
|
681
|
+
@compatibility_class_defs = Layout::ErDiagram.from_graph(
|
|
682
|
+
graph, theme: theme
|
|
683
|
+
).class_defs
|
|
684
|
+
graph[:children].each { |node| render_entity(node, svg) }
|
|
685
|
+
ensure
|
|
686
|
+
@compatibility_class_defs = previous
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def render_entity(node, svg)
|
|
690
|
+
entity = Layout::ErDiagram.entity_from_hash(node, theme: theme)
|
|
691
|
+
styles = entity_styles(entity, @compatibility_class_defs || [])
|
|
692
|
+
group = Svg::Group.new.tap { |item| item.id = "entity-#{entity.id}" }
|
|
693
|
+
group.children << entity_box(entity, styles)
|
|
694
|
+
with_compatibility_styles(styles) do
|
|
695
|
+
render_entity_content(node, node[:metadata] || {}, group)
|
|
696
|
+
end
|
|
697
|
+
svg << group
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
def with_compatibility_styles(styles)
|
|
701
|
+
previous = @compatibility_styles
|
|
702
|
+
@compatibility_styles = styles
|
|
703
|
+
yield
|
|
704
|
+
ensure
|
|
705
|
+
@compatibility_styles = previous
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
def render_entity_content(node, metadata, group)
|
|
709
|
+
entity = Layout::ErDiagram.entity_from_hash(
|
|
710
|
+
node.merge(metadata: metadata), theme: theme
|
|
711
|
+
)
|
|
712
|
+
emit_entity_header(entity, group, @compatibility_styles || {})
|
|
713
|
+
(metadata[:attributes] || []).zip(entity.attributes) do |attribute, row|
|
|
714
|
+
render_attribute(
|
|
715
|
+
entity.x, row.y - row.font_size, entity.width, attribute, group
|
|
716
|
+
)
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
def render_attribute(x_pos, y_pos, _width, attribute, group)
|
|
721
|
+
row = Layout::ErDiagram.attribute_row(
|
|
722
|
+
x_pos, y_pos, attribute, theme: theme
|
|
723
|
+
)
|
|
724
|
+
emit_attribute(row, @compatibility_styles || {}, group)
|
|
725
|
+
y_pos + Layout::ErDiagram::TEXT_LINE_HEIGHT
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
def render_relationships(graph, svg)
|
|
729
|
+
if graph.is_a?(Layout::ErDiagram::Scene)
|
|
730
|
+
return emit_relationships(graph.edges, svg)
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
graph[:edges].each do |edge|
|
|
734
|
+
render_relationship(edge, graph, svg)
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
def render_relationship(edge, graph, svg)
|
|
739
|
+
endpoints = relationship_endpoints(edge, graph)
|
|
740
|
+
return unless endpoints
|
|
741
|
+
|
|
742
|
+
svg << compatibility_relationship_group(edge, *endpoints)
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
def relationship_endpoints(edge, graph)
|
|
746
|
+
source = find_node(graph, edge[:sources]&.first)
|
|
747
|
+
target = find_node(graph, edge[:targets]&.first)
|
|
748
|
+
[source, target] if source && target
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
def compatibility_relationship_group(edge, source, target)
|
|
752
|
+
metadata = edge[:metadata] || {}
|
|
753
|
+
from = calculate_connection_point(source, target)
|
|
754
|
+
to = calculate_connection_point(target, source)
|
|
755
|
+
group = Svg::Group.new.tap { |item| item.id = "rel-#{edge[:id]}" }
|
|
756
|
+
render_relationship_line(
|
|
757
|
+
from, to, metadata[:relationship_type] || "non-identifying", group
|
|
758
|
+
)
|
|
759
|
+
render_compatibility_cardinalities(metadata, from, to, group)
|
|
760
|
+
render_relationship_label(edge, from, to, group)
|
|
761
|
+
group
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
def render_compatibility_cardinalities(metadata, from, to, group)
|
|
765
|
+
if metadata[:cardinality_from]
|
|
766
|
+
render_cardinality(
|
|
767
|
+
from, to, metadata[:cardinality_from], :source, group
|
|
768
|
+
)
|
|
769
|
+
end
|
|
770
|
+
if metadata[:cardinality_to]
|
|
771
|
+
render_cardinality(
|
|
772
|
+
to, from, metadata[:cardinality_to], :target, group
|
|
773
|
+
)
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
def find_node(graph, node_id)
|
|
778
|
+
Layout::ErDiagram.find_node(graph, node_id)
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def calculate_connection_point(from_node, to_node)
|
|
782
|
+
point = Layout::ErDiagram.connection_point(from_node, to_node)
|
|
783
|
+
{ x: point.x, y: point.y }
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
def render_relationship_line(from, to, rel_type, group)
|
|
787
|
+
section = Layout::ErDiagram.section(from, to)
|
|
788
|
+
group.children << relationship_line(section, rel_type)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
def render_cardinality(point, opposite_point, cardinality, _side, group)
|
|
792
|
+
case cardinality
|
|
793
|
+
when "one"
|
|
794
|
+
render_one_marker(point, opposite_point, group)
|
|
795
|
+
when "zero_or_more"
|
|
796
|
+
render_zero_or_more_marker(point, opposite_point, group)
|
|
797
|
+
when "one_or_more"
|
|
798
|
+
render_one_or_more_marker(point, opposite_point, group)
|
|
799
|
+
when "zero_or_one"
|
|
800
|
+
render_zero_or_one_marker(point, opposite_point, group)
|
|
801
|
+
end
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
def render_one_marker(point, opposite_point, group)
|
|
805
|
+
marker = Layout::ErDiagram.one_marker(point, opposite_point)
|
|
806
|
+
emit_marker(marker, group)
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
def render_zero_or_more_marker(point, opposite_point, group)
|
|
810
|
+
render_circle_marker(point, opposite_point, group)
|
|
811
|
+
render_crows_foot(point, opposite_point, group)
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
def render_one_or_more_marker(point, opposite_point, group)
|
|
815
|
+
render_one_marker(point, opposite_point, group)
|
|
816
|
+
render_crows_foot(point, opposite_point, group)
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
def render_zero_or_one_marker(point, opposite_point, group)
|
|
820
|
+
render_circle_marker(point, opposite_point, group)
|
|
821
|
+
render_one_marker(point, opposite_point, group)
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
def render_circle_marker(point, opposite_point, group)
|
|
825
|
+
marker = Layout::ErDiagram.circle_marker(point, opposite_point)
|
|
826
|
+
emit_marker(marker, group)
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
def render_crows_foot(point, opposite_point, group)
|
|
830
|
+
marker = Layout::ErDiagram.crows_marker(point, opposite_point)
|
|
831
|
+
emit_marker(marker, group)
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
def render_relationship_label(edge, from, to, group)
|
|
835
|
+
label = Layout::ErDiagram.edge_label(
|
|
836
|
+
edge, from, to, theme: theme
|
|
837
|
+
)
|
|
838
|
+
group.children << relationship_text(label) if label
|
|
839
|
+
end
|
|
840
|
+
end
|
|
841
|
+
end
|
|
842
|
+
end
|