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,379 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Etassia
|
|
4
|
+
module Renderer
|
|
5
|
+
# Routes one architecture-beta edge around whatever else is in the
|
|
6
|
+
# diagram, instead of drawing straight through it. Geometry only, no
|
|
7
|
+
# SVG. Not an extension of EdgeRouter (don't unify them): EdgeRouter
|
|
8
|
+
# trims two boxes with geometry-derived faces (flowchart); here faces
|
|
9
|
+
# are user-declared (`a:R -- T:b`) with arbitrarily many obstacles.
|
|
10
|
+
#
|
|
11
|
+
# Coordinate-compressed grid + direction-aware Dijkstra: a straight
|
|
12
|
+
# line when already clear, else the shortest bent path that
|
|
13
|
+
# leaves/arrives on the declared faces without crossing an obstacle's
|
|
14
|
+
# interior (source/target boxes exempt only at the anchor - see `route`).
|
|
15
|
+
class ArchitectureEdgeRouter
|
|
16
|
+
# Outer escape line, added only when the natural grid (built purely
|
|
17
|
+
# from existing box edges) has no clear path at all.
|
|
18
|
+
MARGIN = 20
|
|
19
|
+
|
|
20
|
+
# Dijkstra cost added when a hop changes direction, so the search
|
|
21
|
+
# prefers fewer bends over merely fewer hops.
|
|
22
|
+
TURN_PENALTY = 5
|
|
23
|
+
|
|
24
|
+
FACE_NORMAL = {
|
|
25
|
+
"L" => { x: -1, y: 0 },
|
|
26
|
+
"R" => { x: 1, y: 0 },
|
|
27
|
+
"T" => { x: 0, y: -1 },
|
|
28
|
+
"B" => { x: 0, y: 1 },
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
GRID_STEPS = [[1, 0], [-1, 0], [0, 1], [0, -1]].freeze
|
|
32
|
+
|
|
33
|
+
# A binary min-heap keyed on cost, so search_grid's Dijkstra pops the
|
|
34
|
+
# cheapest frontier entry in O(log n) instead of re-sorting the whole
|
|
35
|
+
# frontier on every pop. Ruby's stdlib has no heap; this is the whole
|
|
36
|
+
# of what the search needs from one - push and pop, nothing else.
|
|
37
|
+
class MinHeap
|
|
38
|
+
def initialize
|
|
39
|
+
@entries = []
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def empty?
|
|
43
|
+
@entries.empty?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def push(cost, item)
|
|
47
|
+
@entries << [cost, item]
|
|
48
|
+
sift_up(@entries.length - 1)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Array(Numeric, Object)] the lowest-cost (cost, item) pair
|
|
52
|
+
def pop
|
|
53
|
+
top = @entries.first
|
|
54
|
+
last = @entries.pop
|
|
55
|
+
unless @entries.empty?
|
|
56
|
+
@entries[0] = last
|
|
57
|
+
sift_down(0)
|
|
58
|
+
end
|
|
59
|
+
top
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def sift_up(index)
|
|
65
|
+
while index.positive?
|
|
66
|
+
parent = (index - 1) / 2
|
|
67
|
+
break if @entries[parent][0] <= @entries[index][0]
|
|
68
|
+
|
|
69
|
+
swap_entries(parent, index)
|
|
70
|
+
index = parent
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def sift_down(index)
|
|
75
|
+
size = @entries.length
|
|
76
|
+
loop do
|
|
77
|
+
smallest = smallest_entry(index, size)
|
|
78
|
+
break if smallest == index
|
|
79
|
+
|
|
80
|
+
swap_entries(index, smallest)
|
|
81
|
+
index = smallest
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def smallest_entry(index, size)
|
|
86
|
+
left = (2 * index) + 1
|
|
87
|
+
[index, left, left + 1].select { |entry| entry < size }
|
|
88
|
+
.min_by { |entry| @entries[entry][0] }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def swap_entries(first, second)
|
|
92
|
+
@entries[first], @entries[second] = @entries[second], @entries[first]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
private_constant :MinHeap
|
|
96
|
+
|
|
97
|
+
# @param from [Hash] edge source: point {x:,y:} anchor on the declared
|
|
98
|
+
# face; box {x:,y:,width:,height:} - a clearance obstacle a path may
|
|
99
|
+
# only touch at point, never cross, even though it's never in
|
|
100
|
+
# +obstacles+; side "L"/"R"/"T"/"B" - which face point sits on
|
|
101
|
+
# @param to [Hash] edge target, same shape as +from+
|
|
102
|
+
# @param obstacles [Array<Hash>] every OTHER box to route around (the
|
|
103
|
+
# caller already excludes from/to and any ancestor-or-equal group)
|
|
104
|
+
# @return [Array<Hash>] ordered {x:,y:} points, from[:point] first,
|
|
105
|
+
# to[:point] last. Length 2 means a straight line was clear.
|
|
106
|
+
def route(from:, to:, obstacles:)
|
|
107
|
+
clearance_obstacles = obstacles + [from[:box], to[:box]]
|
|
108
|
+
if straight_clear?(from[:point], to[:point], clearance_obstacles)
|
|
109
|
+
return [from[:point], to[:point]]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
path = shortest_path(from, to, clearance_obstacles)
|
|
113
|
+
return [from[:point], to[:point]] unless path
|
|
114
|
+
|
|
115
|
+
collapse_collinear(path)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def straight_clear?(from_point, to_point, clearance_obstacles)
|
|
121
|
+
clearance_obstacles.none? do |box|
|
|
122
|
+
segment_crosses_box?(box, from_point, to_point)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# General segment-vs-box interior test (the segment need not be
|
|
127
|
+
# axis-aligned - a declared-face-to-declared-face anchor pair rarely
|
|
128
|
+
# is, e.g. an R face to a T face). Finds the sub-range of t in [0,1]
|
|
129
|
+
# where the segment's x is strictly inside the box's x-span AND its y
|
|
130
|
+
# is strictly inside the box's y-span; a border touch does not count
|
|
131
|
+
# as crossing, matching EdgeRouter#crosses_face?'s semantics.
|
|
132
|
+
def segment_crosses_box?(box, first_point, second_point)
|
|
133
|
+
x_interval = horizontal_interval(box, first_point, second_point)
|
|
134
|
+
return false unless x_interval
|
|
135
|
+
|
|
136
|
+
y_interval = vertical_interval(box, first_point, second_point)
|
|
137
|
+
return false unless y_interval
|
|
138
|
+
|
|
139
|
+
lo = [x_interval[0], y_interval[0], 0.0].max
|
|
140
|
+
hi = [x_interval[1], y_interval[1], 1.0].min
|
|
141
|
+
lo < hi
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def horizontal_interval(box, first_point, second_point)
|
|
145
|
+
axis_interval(first_point[:x], second_point[:x], box[:x] || 0,
|
|
146
|
+
right_of(box))
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def vertical_interval(box, first_point, second_point)
|
|
150
|
+
axis_interval(first_point[:y], second_point[:y], box[:y] || 0,
|
|
151
|
+
bottom_of(box))
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# The [t_lo, t_hi] sub-range of t (segment parametrized as
|
|
155
|
+
# c1 + t*(c2-c1)) where the coordinate is strictly between near and
|
|
156
|
+
# far. nil when no such t exists - including a segment that runs
|
|
157
|
+
# exactly along that axis outside (near, far), or exactly along its
|
|
158
|
+
# border, since a border touch is not "strictly between".
|
|
159
|
+
def axis_interval(first_coordinate, second_coordinate, near, far)
|
|
160
|
+
if first_coordinate == second_coordinate
|
|
161
|
+
return infinite_interval if
|
|
162
|
+
first_coordinate.between?(near, far) &&
|
|
163
|
+
![near, far].include?(first_coordinate)
|
|
164
|
+
|
|
165
|
+
return nil
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
step = (second_coordinate - first_coordinate).to_f
|
|
169
|
+
[(near - first_coordinate) / step,
|
|
170
|
+
(far - first_coordinate) / step].sort
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def infinite_interval
|
|
174
|
+
[-Float::INFINITY, Float::INFINITY]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def right_of(box)
|
|
178
|
+
(box[:x] || 0) + (box[:width] || 0)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def bottom_of(box)
|
|
182
|
+
(box[:y] || 0) + (box[:height] || 0)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def shortest_path(from, to, clearance_obstacles)
|
|
186
|
+
grid = build_grid(
|
|
187
|
+
from[:point], to[:point], clearance_obstacles, margin: false
|
|
188
|
+
)
|
|
189
|
+
path = search_grid(grid, from, to, clearance_obstacles)
|
|
190
|
+
return path if path
|
|
191
|
+
|
|
192
|
+
widened = build_grid(
|
|
193
|
+
from[:point], to[:point], clearance_obstacles, margin: true
|
|
194
|
+
)
|
|
195
|
+
search_grid(widened, from, to, clearance_obstacles)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Critical coordinate lines: every obstacle's own edges, plus the two
|
|
199
|
+
# anchor points. With margin: true, one extra line MARGIN beyond the
|
|
200
|
+
# overall extent on each side, clamped to >= 0 so a route can never
|
|
201
|
+
# need a negative coordinate.
|
|
202
|
+
def build_grid(from_point, to_point, clearance_obstacles, margin:)
|
|
203
|
+
xs = [from_point[:x], to_point[:x]]
|
|
204
|
+
ys = [from_point[:y], to_point[:y]]
|
|
205
|
+
add_obstacle_coordinates(xs, ys, clearance_obstacles)
|
|
206
|
+
add_margin_coordinates(xs, ys) if margin
|
|
207
|
+
{ xs: xs.uniq.sort, ys: ys.uniq.sort }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def add_obstacle_coordinates(x_coordinates, y_coordinates, obstacles)
|
|
211
|
+
obstacles.each do |box|
|
|
212
|
+
x_coordinates << (box[:x] || 0) << right_of(box)
|
|
213
|
+
y_coordinates << (box[:y] || 0) << bottom_of(box)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def add_margin_coordinates(x_coordinates, y_coordinates)
|
|
218
|
+
x_coordinates << [x_coordinates.min - MARGIN, 0].max
|
|
219
|
+
x_coordinates << (x_coordinates.max + MARGIN)
|
|
220
|
+
y_coordinates << [y_coordinates.min - MARGIN, 0].max
|
|
221
|
+
y_coordinates << (y_coordinates.max + MARGIN)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Direction-aware Dijkstra over the grid-line intersections. A hop
|
|
225
|
+
# between grid-adjacent points is a valid graph edge iff the short
|
|
226
|
+
# segment it draws is not segment_crosses_box? for any obstacle - the
|
|
227
|
+
# same predicate straight_clear? uses, so grid-edge validity and the
|
|
228
|
+
# straight-line shortcut share one exact test. The first hop out of
|
|
229
|
+
# from[:point] is constrained to FACE_NORMAL[from[:side]]; the
|
|
230
|
+
# accepted goal state requires the last hop into to[:point] to be
|
|
231
|
+
# -FACE_NORMAL[to[:side]].
|
|
232
|
+
def search_grid(grid, from, to, clearance_obstacles)
|
|
233
|
+
xs = grid[:xs]
|
|
234
|
+
ys = grid[:ys]
|
|
235
|
+
start = grid_position(xs, ys, from[:point])
|
|
236
|
+
goal = grid_position(xs, ys, to[:point])
|
|
237
|
+
return nil if start == goal
|
|
238
|
+
|
|
239
|
+
required_first = face_direction(from[:side])
|
|
240
|
+
required_last = face_direction(to[:side]).map(&:-@)
|
|
241
|
+
find_grid_path(grid, start, goal, [required_first, required_last],
|
|
242
|
+
clearance_obstacles)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def grid_position(x_coordinates, y_coordinates, point)
|
|
246
|
+
[x_coordinates.index(point[:x]), y_coordinates.index(point[:y])]
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def face_direction(side)
|
|
250
|
+
FACE_NORMAL.fetch(side).values_at(:x, :y)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def find_grid_path(grid, start, goal, directions, clearance_obstacles)
|
|
254
|
+
required_first, required_last = directions
|
|
255
|
+
goal_state = [goal[0], goal[1], required_last]
|
|
256
|
+
context = search_context(start, grid)
|
|
257
|
+
until context[:frontier].empty?
|
|
258
|
+
path = visit_grid_state(context, goal_state, required_first,
|
|
259
|
+
clearance_obstacles)
|
|
260
|
+
return path if path
|
|
261
|
+
end
|
|
262
|
+
nil
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def search_context(start, grid)
|
|
266
|
+
frontier = MinHeap.new
|
|
267
|
+
frontier.push(0, [start[0], start[1], nil])
|
|
268
|
+
{
|
|
269
|
+
grid: grid, frontier: frontier, previous: {},
|
|
270
|
+
distances: { [start[0], start[1], nil] => 0 }
|
|
271
|
+
}
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def visit_grid_state(context, goal_state, required_first,
|
|
275
|
+
clearance_obstacles)
|
|
276
|
+
cost, state = context[:frontier].pop
|
|
277
|
+
return if stale_state?(context, state, cost)
|
|
278
|
+
|
|
279
|
+
path = reconstruct_goal(context, state, goal_state)
|
|
280
|
+
return path if path
|
|
281
|
+
|
|
282
|
+
expand_grid_state(context, state, cost, required_first,
|
|
283
|
+
clearance_obstacles)
|
|
284
|
+
nil
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def stale_state?(context, state, cost)
|
|
288
|
+
cost > context[:distances].fetch(state, Float::INFINITY)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def reconstruct_goal(context, state, goal_state)
|
|
292
|
+
return unless state == goal_state
|
|
293
|
+
|
|
294
|
+
grid = context[:grid]
|
|
295
|
+
reconstruct(context[:previous], state, grid[:xs], grid[:ys])
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def expand_grid_state(context, state, cost, required_first,
|
|
299
|
+
clearance_obstacles)
|
|
300
|
+
grid = context[:grid]
|
|
301
|
+
moves = expand(state, grid, required_first, clearance_obstacles)
|
|
302
|
+
moves.each { |move| relax_grid_move(move, cost, state, context) }
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def relax_grid_move(move, cost, state, context)
|
|
306
|
+
next_state, step_cost = move
|
|
307
|
+
new_cost = cost + step_cost
|
|
308
|
+
distances = context[:distances]
|
|
309
|
+
return if new_cost >= distances.fetch(next_state, Float::INFINITY)
|
|
310
|
+
|
|
311
|
+
distances[next_state] = new_cost
|
|
312
|
+
context[:previous][next_state] = state
|
|
313
|
+
context[:frontier].push(new_cost, next_state)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def expand(state, grid, required_first, clearance_obstacles)
|
|
317
|
+
GRID_STEPS.filter_map do |step|
|
|
318
|
+
grid_move(state, step, grid, required_first, clearance_obstacles)
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def grid_move(state, step, grid, required_first, clearance_obstacles)
|
|
323
|
+
x_index, y_index, direction = state
|
|
324
|
+
return if direction.nil? && step != required_first
|
|
325
|
+
|
|
326
|
+
next_x, next_y = next_grid_position(x_index, y_index, step)
|
|
327
|
+
return unless grid_position_valid?(next_x, next_y, grid)
|
|
328
|
+
|
|
329
|
+
points = grid_segment(x_index, y_index, next_x, next_y, grid)
|
|
330
|
+
return unless straight_clear?(*points, clearance_obstacles)
|
|
331
|
+
|
|
332
|
+
[[next_x, next_y, step], grid_step_cost(direction, step)]
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def next_grid_position(x_index, y_index, step)
|
|
336
|
+
[x_index + step[0], y_index + step[1]]
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def grid_position_valid?(x_index, y_index, grid)
|
|
340
|
+
x_index.between?(0, grid[:xs].length - 1) &&
|
|
341
|
+
y_index.between?(0, grid[:ys].length - 1)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def grid_segment(x_index, y_index, next_x, next_y, grid)
|
|
345
|
+
[{ x: grid[:xs][x_index], y: grid[:ys][y_index] },
|
|
346
|
+
{ x: grid[:xs][next_x], y: grid[:ys][next_y] }]
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def grid_step_cost(direction, next_direction)
|
|
350
|
+
direction && direction != next_direction ? 1 + TURN_PENALTY : 1
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def reconstruct(prev, state, x_coordinates, y_coordinates)
|
|
354
|
+
path = [state]
|
|
355
|
+
path << prev[path.last] while prev.key?(path.last)
|
|
356
|
+
path.reverse.map do |x_index, y_index, _direction|
|
|
357
|
+
{ x: x_coordinates[x_index], y: y_coordinates[y_index] }
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Drops points that don't represent an actual direction change, so
|
|
362
|
+
# the returned path only contains real bends.
|
|
363
|
+
def collapse_collinear(points)
|
|
364
|
+
return points if points.length <= 2
|
|
365
|
+
|
|
366
|
+
result = [points.first]
|
|
367
|
+
points.each_cons(3) do |before, at, after|
|
|
368
|
+
result << at if direction(before, at) != direction(at, after)
|
|
369
|
+
end
|
|
370
|
+
result << points.last
|
|
371
|
+
result
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def direction(from, to)
|
|
375
|
+
[to[:x] <=> from[:x], to[:y] <=> from[:y]]
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../error"
|
|
4
|
+
require_relative "../error/render_error"
|
|
5
|
+
|
|
6
|
+
module Etassia
|
|
7
|
+
module Renderer
|
|
8
|
+
# Abstract base class for diagram renderers.
|
|
9
|
+
#
|
|
10
|
+
# Renderers convert laid-out graph structures (with computed positions)
|
|
11
|
+
# into SVG documents using the Svg builder classes. Each diagram type has
|
|
12
|
+
# its own renderer that maps graph elements to appropriate SVG shapes.
|
|
13
|
+
#
|
|
14
|
+
# The renderer implements the template method pattern, defining the
|
|
15
|
+
# overall rendering pipeline while allowing subclasses to customize
|
|
16
|
+
# specific rendering steps.
|
|
17
|
+
#
|
|
18
|
+
# @example Define a custom renderer
|
|
19
|
+
# class Flowchart < Renderer::Base
|
|
20
|
+
# def render(graph)
|
|
21
|
+
# svg = create_document(graph)
|
|
22
|
+
# render_nodes(graph, svg)
|
|
23
|
+
# render_edges(graph, svg)
|
|
24
|
+
# svg
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# protected
|
|
28
|
+
#
|
|
29
|
+
# def render_nodes(graph, svg)
|
|
30
|
+
# # Render graph nodes as SVG shapes
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# def render_edges(graph, svg)
|
|
34
|
+
# # Render graph edges as SVG paths
|
|
35
|
+
# end
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# @abstract Subclass and implement rendering methods
|
|
39
|
+
class Base
|
|
40
|
+
# Factor on a measured text width for room around a label; it is NOT a
|
|
41
|
+
# bound. A viewer that lacks Arial substitutes its own sans-serif:
|
|
42
|
+
# DejaVu Sans and Verdana measured up to 1.19 times Arial's width.
|
|
43
|
+
SUBSTITUTE_FONT_HEADROOM = 1.2
|
|
44
|
+
private_constant :SUBSTITUTE_FONT_HEADROOM
|
|
45
|
+
|
|
46
|
+
attr_accessor :theme
|
|
47
|
+
|
|
48
|
+
# Creates a new renderer instance.
|
|
49
|
+
#
|
|
50
|
+
# @param theme [Theme, nil] theme to use for rendering
|
|
51
|
+
def initialize(theme: nil)
|
|
52
|
+
@theme = theme || Theme::Registry.get(:default)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Renders a laid-out graph to an SVG document.
|
|
56
|
+
#
|
|
57
|
+
# This method should be overridden by subclasses to implement
|
|
58
|
+
# diagram-specific rendering logic.
|
|
59
|
+
#
|
|
60
|
+
# @param graph [Object] the laid-out elkrb graph with positions
|
|
61
|
+
# @return [Svg::Document] the rendered SVG document
|
|
62
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
63
|
+
def render(graph)
|
|
64
|
+
raise NotImplementedError,
|
|
65
|
+
"#{self.class} must implement #render(graph)"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
protected
|
|
69
|
+
|
|
70
|
+
# Creates an SVG document from final Scene canvas dimensions.
|
|
71
|
+
#
|
|
72
|
+
# Scene coordinates already include their framing. The renderer must not
|
|
73
|
+
# add padding or reuse a caller-supplied viewBox origin.
|
|
74
|
+
#
|
|
75
|
+
# @param scene [Layout::Scene] final canvas geometry
|
|
76
|
+
# @param overflow [String, nil] SVG `overflow` presentation attribute
|
|
77
|
+
# for the root element, e.g. `'hidden'`
|
|
78
|
+
# @return [Svg::Document] new SVG document
|
|
79
|
+
def create_document(scene, overflow: nil)
|
|
80
|
+
dimensions = document_dimensions(scene)
|
|
81
|
+
Svg::Document.new(
|
|
82
|
+
width: scene.width, height: scene.height,
|
|
83
|
+
view_box: "0 0 #{dimensions.join(' ')}", overflow: overflow
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def document_dimensions(scene)
|
|
88
|
+
dimensions = [scene.width, scene.height].map do |value|
|
|
89
|
+
value.to_i == value ? value.to_i : value
|
|
90
|
+
end
|
|
91
|
+
existing = scene.view_box.to_s.split.last(2)
|
|
92
|
+
{ existing.map(&:to_f) => existing }.fetch(
|
|
93
|
+
[scene.width, scene.height], dimensions
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Room to reserve for a text label: its measured width with headroom
|
|
98
|
+
# for a substituted font. Not a bound; see TextMeasurement for what the
|
|
99
|
+
# measurement itself does and does not cover.
|
|
100
|
+
#
|
|
101
|
+
# @param text [String] label text
|
|
102
|
+
# @param font_size [Numeric] the size the label is drawn at
|
|
103
|
+
# @return [Float] width in pixels
|
|
104
|
+
def reserved_text_width(text, font_size)
|
|
105
|
+
TextMeasurement.measure(text, font_size: font_size)[:width] *
|
|
106
|
+
SUBSTITUTE_FONT_HEADROOM
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Calculates the total width needed for the diagram.
|
|
110
|
+
#
|
|
111
|
+
# Subclasses should override this to compute width from graph.
|
|
112
|
+
#
|
|
113
|
+
# @param graph [Object] the graph
|
|
114
|
+
# @return [Numeric] the width in pixels
|
|
115
|
+
def calculate_width(_graph)
|
|
116
|
+
800 # Default width
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Calculates the total height needed for the diagram.
|
|
120
|
+
#
|
|
121
|
+
# Subclasses should override this to compute height from graph.
|
|
122
|
+
#
|
|
123
|
+
# @param graph [Object] the graph
|
|
124
|
+
# @return [Numeric] the height in pixels
|
|
125
|
+
def calculate_height(_graph)
|
|
126
|
+
600 # Default height
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Helper methods for accessing theme properties
|
|
130
|
+
|
|
131
|
+
# Gets a color property from the theme.
|
|
132
|
+
#
|
|
133
|
+
# @param property [Symbol] property name
|
|
134
|
+
# @return [String, nil] color value
|
|
135
|
+
def theme_color(property)
|
|
136
|
+
theme&.colors&.send(property)
|
|
137
|
+
rescue NoMethodError
|
|
138
|
+
nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Gets a typography property from the theme.
|
|
142
|
+
#
|
|
143
|
+
# @param property [Symbol] property name
|
|
144
|
+
# @return [String, Float, nil] typography value
|
|
145
|
+
def theme_typography(property)
|
|
146
|
+
theme&.typography&.send(property)
|
|
147
|
+
rescue NoMethodError
|
|
148
|
+
nil
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Gets a shape property from the theme.
|
|
152
|
+
#
|
|
153
|
+
# @param property [Symbol] property name
|
|
154
|
+
# @return [Float, String, nil] shape value
|
|
155
|
+
def theme_shape(property)
|
|
156
|
+
theme&.shapes&.send(property)
|
|
157
|
+
rescue NoMethodError
|
|
158
|
+
nil
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Gets a spacing property from the theme.
|
|
162
|
+
#
|
|
163
|
+
# @param property [Symbol] property name
|
|
164
|
+
# @return [Float, nil] spacing value
|
|
165
|
+
def theme_spacing(property)
|
|
166
|
+
theme&.spacing&.send(property)
|
|
167
|
+
rescue NoMethodError
|
|
168
|
+
nil
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Gets an effect property from the theme.
|
|
172
|
+
#
|
|
173
|
+
# @param property [Symbol] property name
|
|
174
|
+
# @return [Boolean, Float, String, nil] effect value
|
|
175
|
+
def theme_effect(property)
|
|
176
|
+
theme&.effects&.send(property)
|
|
177
|
+
rescue NoMethodError
|
|
178
|
+
nil
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Applies theme styles to a node element.
|
|
182
|
+
#
|
|
183
|
+
# @param element [Svg::Element] SVG element
|
|
184
|
+
# @return [void]
|
|
185
|
+
def apply_theme_to_node(element)
|
|
186
|
+
element.fill = theme_color(:node_fill) if theme_color(:node_fill)
|
|
187
|
+
element.stroke = theme_color(:node_stroke) if theme_color(:node_stroke)
|
|
188
|
+
if theme_shape(:stroke_width)
|
|
189
|
+
element.stroke_width = theme_shape(:stroke_width).to_s
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Applies theme styles to an edge element.
|
|
194
|
+
#
|
|
195
|
+
# @param element [Svg::Element] SVG element
|
|
196
|
+
# @return [void]
|
|
197
|
+
def apply_theme_to_edge(element)
|
|
198
|
+
element.stroke = theme_color(:edge_stroke) if theme_color(:edge_stroke)
|
|
199
|
+
if theme_shape(:stroke_width)
|
|
200
|
+
element.stroke_width = theme_shape(:stroke_width).to_s
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Applies theme styles to a text element.
|
|
205
|
+
#
|
|
206
|
+
# @param element [Svg::Text] SVG text element
|
|
207
|
+
# @return [void]
|
|
208
|
+
def apply_theme_to_text(element)
|
|
209
|
+
element.fill = theme_color(:label_text) if theme_color(:label_text)
|
|
210
|
+
if theme_typography(:font_family)
|
|
211
|
+
element.font_family = theme_typography(:font_family)
|
|
212
|
+
end
|
|
213
|
+
if theme_typography(:font_size_normal)
|
|
214
|
+
element.font_size = theme_typography(:font_size_normal).to_s
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Creates default style for SVG elements.
|
|
219
|
+
#
|
|
220
|
+
# @param element_type [Symbol] type of element (:node, :edge, :text)
|
|
221
|
+
# @return [Svg::Style] style object
|
|
222
|
+
def default_style(element_type)
|
|
223
|
+
case element_type
|
|
224
|
+
when :node
|
|
225
|
+
default_node_style
|
|
226
|
+
when :edge
|
|
227
|
+
default_edge_style
|
|
228
|
+
when :text
|
|
229
|
+
default_text_style
|
|
230
|
+
else
|
|
231
|
+
Svg::Style.new
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def default_node_style
|
|
236
|
+
Svg::Style.new.tap do |style|
|
|
237
|
+
style.fill = default_style_color(:node_fill)
|
|
238
|
+
style.stroke = default_style_color(:node_stroke)
|
|
239
|
+
style.stroke_width = (theme_shape(:stroke_width) || 2).to_s
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def default_edge_style
|
|
244
|
+
Svg::Style.new.tap do |style|
|
|
245
|
+
style.fill = "none"
|
|
246
|
+
style.stroke = default_style_color(:edge_stroke)
|
|
247
|
+
style.stroke_width = (theme_shape(:stroke_width) || 2).to_s
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def default_text_style
|
|
252
|
+
Svg::Style.new.tap do |style|
|
|
253
|
+
style.fill = default_style_color(:label_text)
|
|
254
|
+
style.font_family =
|
|
255
|
+
theme_typography(:font_family) || "Arial, sans-serif"
|
|
256
|
+
style.font_size = (theme_typography(:font_size_normal) || 14).to_s
|
|
257
|
+
style.text_anchor = "middle"
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def default_style_color(property)
|
|
262
|
+
return theme_color(property) if theme
|
|
263
|
+
|
|
264
|
+
Theme::Registry.get(:default).colors.public_send(property)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Creates an SVG path data string from bend points.
|
|
268
|
+
#
|
|
269
|
+
# @param start_point [Hash] hash with :x and :y keys
|
|
270
|
+
# @param end_point [Hash] hash with :x and :y keys
|
|
271
|
+
# @param bend_points [Array<Hash>] array of hashes with :x and :y
|
|
272
|
+
# @return [String] SVG path data string
|
|
273
|
+
def create_path_data(start_point, end_point, bend_points = [])
|
|
274
|
+
points = [start_point] + bend_points + [end_point]
|
|
275
|
+
path_parts = ["M #{points[0][:x]} #{points[0][:y]}"]
|
|
276
|
+
|
|
277
|
+
points[1..].each do |point|
|
|
278
|
+
path_parts << "L #{point[:x]} #{point[:y]}"
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
path_parts.join(" ")
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Adds an arrow marker to the SVG document.
|
|
285
|
+
#
|
|
286
|
+
# @param svg [Svg::Document] the SVG document
|
|
287
|
+
# @param id [String] unique identifier for the marker
|
|
288
|
+
# @return [void]
|
|
289
|
+
def add_arrow_marker(svg, id: "arrow")
|
|
290
|
+
# This is a placeholder for actual marker implementation
|
|
291
|
+
# Subclasses can implement this to add arrow markers
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|