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.
Files changed (727) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +140 -0
  3. data/LICENSE +25 -0
  4. data/README.adoc +436 -0
  5. data/exe/etassia +7 -0
  6. data/lib/etassia/cli.rb +187 -0
  7. data/lib/etassia/commands/batch.rb +210 -0
  8. data/lib/etassia/commands/notation_loader.rb +22 -0
  9. data/lib/etassia/commands/render.rb +98 -0
  10. data/lib/etassia/commands/types.rb +33 -0
  11. data/lib/etassia/commands/version.rb +24 -0
  12. data/lib/etassia/corpus_type_names.rb +52 -0
  13. data/lib/etassia/diagram/architecture.rb +108 -0
  14. data/lib/etassia/diagram/base.rb +61 -0
  15. data/lib/etassia/diagram/block.rb +102 -0
  16. data/lib/etassia/diagram/c4.rb +337 -0
  17. data/lib/etassia/diagram/class_diagram.rb +466 -0
  18. data/lib/etassia/diagram/class_namespace.rb +17 -0
  19. data/lib/etassia/diagram/class_note.rb +17 -0
  20. data/lib/etassia/diagram/containment.rb +107 -0
  21. data/lib/etassia/diagram/er_diagram.rb +317 -0
  22. data/lib/etassia/diagram/error.rb +38 -0
  23. data/lib/etassia/diagram/flowchart.rb +419 -0
  24. data/lib/etassia/diagram/flowchart_label_text.rb +63 -0
  25. data/lib/etassia/diagram/gantt.rb +94 -0
  26. data/lib/etassia/diagram/generic_text.rb +30 -0
  27. data/lib/etassia/diagram/git_graph.rb +57 -0
  28. data/lib/etassia/diagram/info.rb +38 -0
  29. data/lib/etassia/diagram/kanban.rb +198 -0
  30. data/lib/etassia/diagram/mindmap.rb +71 -0
  31. data/lib/etassia/diagram/packet.rb +93 -0
  32. data/lib/etassia/diagram/pie.rb +115 -0
  33. data/lib/etassia/diagram/quadrant.rb +135 -0
  34. data/lib/etassia/diagram/radar.rb +65 -0
  35. data/lib/etassia/diagram/requirement.rb +168 -0
  36. data/lib/etassia/diagram/sankey.rb +217 -0
  37. data/lib/etassia/diagram/sequence.rb +299 -0
  38. data/lib/etassia/diagram/sequence_box.rb +18 -0
  39. data/lib/etassia/diagram/sequence_frame.rb +37 -0
  40. data/lib/etassia/diagram/sequence_frame_section.rb +23 -0
  41. data/lib/etassia/diagram/sequence_text.rb +69 -0
  42. data/lib/etassia/diagram/sequence_wrap.rb +23 -0
  43. data/lib/etassia/diagram/state_diagram.rb +282 -0
  44. data/lib/etassia/diagram/timeline.rb +183 -0
  45. data/lib/etassia/diagram/treemap.rb +95 -0
  46. data/lib/etassia/diagram/user_journey.rb +153 -0
  47. data/lib/etassia/diagram/xy_chart.rb +90 -0
  48. data/lib/etassia/diagram.rb +8 -0
  49. data/lib/etassia/diagram_registry.rb +34 -0
  50. data/lib/etassia/engine.rb +334 -0
  51. data/lib/etassia/error/diagram_type_error.rb +10 -0
  52. data/lib/etassia/error/layout_error.rb +12 -0
  53. data/lib/etassia/error/notation_registration_error.rb +10 -0
  54. data/lib/etassia/error/parse_error.rb +11 -0
  55. data/lib/etassia/error/pipeline_error.rb +10 -0
  56. data/lib/etassia/error/render_error.rb +12 -0
  57. data/lib/etassia/error/score_error.rb +12 -0
  58. data/lib/etassia/error.rb +10 -0
  59. data/lib/etassia/error_report.rb +69 -0
  60. data/lib/etassia/ir/data.rb +39 -0
  61. data/lib/etassia/ir/data_value.rb +35 -0
  62. data/lib/etassia/ir/dimension.rb +10 -0
  63. data/lib/etassia/ir/edge.rb +34 -0
  64. data/lib/etassia/ir/graph.rb +28 -0
  65. data/lib/etassia/ir/integrity.rb +80 -0
  66. data/lib/etassia/ir/item.rb +46 -0
  67. data/lib/etassia/ir/model.rb +56 -0
  68. data/lib/etassia/ir/node.rb +10 -0
  69. data/lib/etassia/ir/placement.rb +47 -0
  70. data/lib/etassia/ir/prepositioned.rb +25 -0
  71. data/lib/etassia/ir/prepositioned_item.rb +26 -0
  72. data/lib/etassia/ir/property_set.rb +43 -0
  73. data/lib/etassia/ir/scalar.rb +25 -0
  74. data/lib/etassia/ir/series.rb +10 -0
  75. data/lib/etassia/ir.rb +17 -0
  76. data/lib/etassia/js_math.rb +511 -0
  77. data/lib/etassia/js_number.rb +64 -0
  78. data/lib/etassia/layout/architecture.rb +1472 -0
  79. data/lib/etassia/layout/base.rb +276 -0
  80. data/lib/etassia/layout/block.rb +738 -0
  81. data/lib/etassia/layout/c4.rb +876 -0
  82. data/lib/etassia/layout/c4_boundary_metrics.rb +35 -0
  83. data/lib/etassia/layout/c4_bounds.rb +106 -0
  84. data/lib/etassia/layout/c4_intersection.rb +77 -0
  85. data/lib/etassia/layout/c4_placement.rb +135 -0
  86. data/lib/etassia/layout/c4_shape_metrics.rb +97 -0
  87. data/lib/etassia/layout/c4_shape_outline.rb +67 -0
  88. data/lib/etassia/layout/c4_stereotype.rb +30 -0
  89. data/lib/etassia/layout/c4_text.rb +54 -0
  90. data/lib/etassia/layout/class_cell_placement.rb +148 -0
  91. data/lib/etassia/layout/class_diagram.rb +1116 -0
  92. data/lib/etassia/layout/class_diagram_namespaces.rb +26 -0
  93. data/lib/etassia/layout/class_diagram_notes.rb +26 -0
  94. data/lib/etassia/layout/class_name_advances.rb +31 -0
  95. data/lib/etassia/layout/class_namespace_box.rb +21 -0
  96. data/lib/etassia/layout/class_namespace_nodes.rb +197 -0
  97. data/lib/etassia/layout/class_namespace_placement.rb +71 -0
  98. data/lib/etassia/layout/class_note.rb +44 -0
  99. data/lib/etassia/layout/class_note_nodes.rb +84 -0
  100. data/lib/etassia/layout/elk_notices.rb +48 -0
  101. data/lib/etassia/layout/elk_placement.rb +197 -0
  102. data/lib/etassia/layout/er_diagram.rb +1002 -0
  103. data/lib/etassia/layout/er_diagram_adjacency.rb +20 -0
  104. data/lib/etassia/layout/er_diagram_alignment.rb +72 -0
  105. data/lib/etassia/layout/er_diagram_compaction.rb +85 -0
  106. data/lib/etassia/layout/er_diagram_conflicts.rb +70 -0
  107. data/lib/etassia/layout/er_diagram_coordinates.rb +104 -0
  108. data/lib/etassia/layout/er_diagram_entity_size.rb +82 -0
  109. data/lib/etassia/layout/er_diagram_layering.rb +109 -0
  110. data/lib/etassia/layout/er_diagram_ordering.rb +143 -0
  111. data/lib/etassia/layout/er_diagram_placement.rb +163 -0
  112. data/lib/etassia/layout/er_diagram_placement_node.rb +22 -0
  113. data/lib/etassia/layout/error.rb +133 -0
  114. data/lib/etassia/layout/flowchart/brandes_koepf.rb +208 -0
  115. data/lib/etassia/layout/flowchart/cluster_reach.rb +70 -0
  116. data/lib/etassia/layout/flowchart/component_packing.rb +129 -0
  117. data/lib/etassia/layout/flowchart/directed_placement.rb +428 -0
  118. data/lib/etassia/layout/flowchart/edge_chains.rb +111 -0
  119. data/lib/etassia/layout/flowchart/edge_order.rb +34 -0
  120. data/lib/etassia/layout/flowchart/edge_router.rb +533 -0
  121. data/lib/etassia/layout/flowchart/label_backdrop_geometry.rb +62 -0
  122. data/lib/etassia/layout/flowchart/layer_spacing.rb +85 -0
  123. data/lib/etassia/layout/flowchart/level_edges.rb +74 -0
  124. data/lib/etassia/layout/flowchart/level_frame.rb +47 -0
  125. data/lib/etassia/layout/flowchart/minimum_rank_spacing.rb +74 -0
  126. data/lib/etassia/layout/flowchart/row_centering.rb +229 -0
  127. data/lib/etassia/layout/flowchart.rb +1290 -0
  128. data/lib/etassia/layout/gantt.rb +814 -0
  129. data/lib/etassia/layout/gantt_duration.rb +49 -0
  130. data/lib/etassia/layout/gantt_exclusions.rb +74 -0
  131. data/lib/etassia/layout/gantt_ticks.rb +165 -0
  132. data/lib/etassia/layout/git_graph.rb +1075 -0
  133. data/lib/etassia/layout/grid.rb +165 -0
  134. data/lib/etassia/layout/info.rb +113 -0
  135. data/lib/etassia/layout/kanban.rb +452 -0
  136. data/lib/etassia/layout/kanban_card_text.rb +95 -0
  137. data/lib/etassia/layout/mindmap.rb +425 -0
  138. data/lib/etassia/layout/mindmap_cose.rb +81 -0
  139. data/lib/etassia/layout/mindmap_cose_body.rb +93 -0
  140. data/lib/etassia/layout/mindmap_cose_geometry.rb +190 -0
  141. data/lib/etassia/layout/mindmap_cose_grid.rb +96 -0
  142. data/lib/etassia/layout/mindmap_cose_link.rb +42 -0
  143. data/lib/etassia/layout/mindmap_cose_relaxation.rb +166 -0
  144. data/lib/etassia/layout/mindmap_cose_seed.rb +93 -0
  145. data/lib/etassia/layout/mindmap_cose_wedge.rb +37 -0
  146. data/lib/etassia/layout/mindmap_node_size.rb +130 -0
  147. data/lib/etassia/layout/packet.rb +285 -0
  148. data/lib/etassia/layout/pie.rb +305 -0
  149. data/lib/etassia/layout/pie_legend.rb +64 -0
  150. data/lib/etassia/layout/pie_legend_entry.rb +21 -0
  151. data/lib/etassia/layout/quadrant.rb +402 -0
  152. data/lib/etassia/layout/radar.rb +442 -0
  153. data/lib/etassia/layout/requirement.rb +765 -0
  154. data/lib/etassia/layout/sankey.rb +501 -0
  155. data/lib/etassia/layout/scene.rb +18 -0
  156. data/lib/etassia/layout/sequence/actor_gaps.rb +151 -0
  157. data/lib/etassia/layout/sequence/box_shape.rb +23 -0
  158. data/lib/etassia/layout/sequence/creations.rb +73 -0
  159. data/lib/etassia/layout/sequence/frame_divider.rb +22 -0
  160. data/lib/etassia/layout/sequence/frame_placement.rb +424 -0
  161. data/lib/etassia/layout/sequence/frame_reader.rb +75 -0
  162. data/lib/etassia/layout/sequence/frame_shape.rb +45 -0
  163. data/lib/etassia/layout/sequence/geometry.rb +24 -0
  164. data/lib/etassia/layout/sequence/message_rows.rb +165 -0
  165. data/lib/etassia/layout/sequence/note.rb +16 -0
  166. data/lib/etassia/layout/sequence/note_box.rb +114 -0
  167. data/lib/etassia/layout/sequence/note_placement.rb +223 -0
  168. data/lib/etassia/layout/sequence/text_width.rb +69 -0
  169. data/lib/etassia/layout/sequence/text_wrap.rb +93 -0
  170. data/lib/etassia/layout/sequence.rb +936 -0
  171. data/lib/etassia/layout/state_diagram.rb +1277 -0
  172. data/lib/etassia/layout/timeline.rb +165 -0
  173. data/lib/etassia/layout/timeline_card.rb +142 -0
  174. data/lib/etassia/layout/timeline_flow.rb +164 -0
  175. data/lib/etassia/layout/timeline_line.rb +33 -0
  176. data/lib/etassia/layout/timeline_rounding.rb +19 -0
  177. data/lib/etassia/layout/timeline_text.rb +105 -0
  178. data/lib/etassia/layout/treemap.rb +303 -0
  179. data/lib/etassia/layout/treemap_placement.rb +52 -0
  180. data/lib/etassia/layout/treemap_squarify.rb +108 -0
  181. data/lib/etassia/layout/user_journey.rb +300 -0
  182. data/lib/etassia/layout/user_journey_face.rb +33 -0
  183. data/lib/etassia/layout/user_journey_face_geometry.rb +80 -0
  184. data/lib/etassia/layout/user_journey_geometry.rb +336 -0
  185. data/lib/etassia/layout/user_journey_legend.rb +84 -0
  186. data/lib/etassia/layout/xy_chart.rb +620 -0
  187. data/lib/etassia/layout.rb +19 -0
  188. data/lib/etassia/lint_debt_scoreboard.rb +219 -0
  189. data/lib/etassia/markdown_text/emphasis_simulator.rb +373 -0
  190. data/lib/etassia/markdown_text.rb +393 -0
  191. data/lib/etassia/notation/builtin.rb +39 -0
  192. data/lib/etassia/notation/entry.rb +15 -0
  193. data/lib/etassia/notation/mermaid/class_diagram_config.rb +42 -0
  194. data/lib/etassia/notation/mermaid/git_graph_config.rb +46 -0
  195. data/lib/etassia/notation/mermaid/ir_adapter.rb +27 -0
  196. data/lib/etassia/notation/mermaid/ir_adapters/architecture.rb +196 -0
  197. data/lib/etassia/notation/mermaid/ir_adapters/block.rb +138 -0
  198. data/lib/etassia/notation/mermaid/ir_adapters/c4.rb +258 -0
  199. data/lib/etassia/notation/mermaid/ir_adapters/class_diagram.rb +257 -0
  200. data/lib/etassia/notation/mermaid/ir_adapters/er_diagram.rb +220 -0
  201. data/lib/etassia/notation/mermaid/ir_adapters/error.rb +32 -0
  202. data/lib/etassia/notation/mermaid/ir_adapters/flowchart.rb +208 -0
  203. data/lib/etassia/notation/mermaid/ir_adapters/gantt.rb +236 -0
  204. data/lib/etassia/notation/mermaid/ir_adapters/git_graph.rb +186 -0
  205. data/lib/etassia/notation/mermaid/ir_adapters/info.rb +34 -0
  206. data/lib/etassia/notation/mermaid/ir_adapters/kanban.rb +110 -0
  207. data/lib/etassia/notation/mermaid/ir_adapters/mindmap.rb +76 -0
  208. data/lib/etassia/notation/mermaid/ir_adapters/packet.rb +51 -0
  209. data/lib/etassia/notation/mermaid/ir_adapters/pie.rb +71 -0
  210. data/lib/etassia/notation/mermaid/ir_adapters/quadrant.rb +116 -0
  211. data/lib/etassia/notation/mermaid/ir_adapters/radar.rb +142 -0
  212. data/lib/etassia/notation/mermaid/ir_adapters/requirement.rb +237 -0
  213. data/lib/etassia/notation/mermaid/ir_adapters/sankey.rb +74 -0
  214. data/lib/etassia/notation/mermaid/ir_adapters/sequence.rb +304 -0
  215. data/lib/etassia/notation/mermaid/ir_adapters/state_diagram.rb +225 -0
  216. data/lib/etassia/notation/mermaid/ir_adapters/timeline.rb +93 -0
  217. data/lib/etassia/notation/mermaid/ir_adapters/treemap.rb +143 -0
  218. data/lib/etassia/notation/mermaid/ir_adapters/user_journey.rb +127 -0
  219. data/lib/etassia/notation/mermaid/ir_adapters/xychart.rb +131 -0
  220. data/lib/etassia/notation/mermaid.rb +399 -0
  221. data/lib/etassia/notation/parsed.rb +31 -0
  222. data/lib/etassia/notation/plantuml/activity/arrow_head.rb +45 -0
  223. data/lib/etassia/notation/plantuml/activity/arrow_style.rb +53 -0
  224. data/lib/etassia/notation/plantuml/activity/bar_columns.rb +75 -0
  225. data/lib/etassia/notation/plantuml/activity/box.rb +60 -0
  226. data/lib/etassia/notation/plantuml/activity/builder.rb +77 -0
  227. data/lib/etassia/notation/plantuml/activity/canvas.rb +117 -0
  228. data/lib/etassia/notation/plantuml/activity/column_centres.rb +151 -0
  229. data/lib/etassia/notation/plantuml/activity/columns.rb +224 -0
  230. data/lib/etassia/notation/plantuml/activity/diagram.rb +35 -0
  231. data/lib/etassia/notation/plantuml/activity/edge.rb +35 -0
  232. data/lib/etassia/notation/plantuml/activity/edge_marks.rb +68 -0
  233. data/lib/etassia/notation/plantuml/activity/exits.rb +61 -0
  234. data/lib/etassia/notation/plantuml/activity/frame_marks.rb +116 -0
  235. data/lib/etassia/notation/plantuml/activity/lane_steps.rb +82 -0
  236. data/lib/etassia/notation/plantuml/activity/lane_titles.rb +46 -0
  237. data/lib/etassia/notation/plantuml/activity/layout.rb +50 -0
  238. data/lib/etassia/notation/plantuml/activity/legacy_parser.rb +181 -0
  239. data/lib/etassia/notation/plantuml/activity/legacy_row_gaps.rb +65 -0
  240. data/lib/etassia/notation/plantuml/activity/mark.rb +23 -0
  241. data/lib/etassia/notation/plantuml/activity/marks.rb +62 -0
  242. data/lib/etassia/notation/plantuml/activity/metrics.rb +169 -0
  243. data/lib/etassia/notation/plantuml/activity/new_parser/blocks.rb +166 -0
  244. data/lib/etassia/notation/plantuml/activity/new_parser.rb +177 -0
  245. data/lib/etassia/notation/plantuml/activity/node.rb +50 -0
  246. data/lib/etassia/notation/plantuml/activity/node_marks.rb +174 -0
  247. data/lib/etassia/notation/plantuml/activity/note.rb +27 -0
  248. data/lib/etassia/notation/plantuml/activity/parser.rb +51 -0
  249. data/lib/etassia/notation/plantuml/activity/placement.rb +327 -0
  250. data/lib/etassia/notation/plantuml/activity/ranking.rb +142 -0
  251. data/lib/etassia/notation/plantuml/activity/reach.rb +58 -0
  252. data/lib/etassia/notation/plantuml/activity/renderer.rb +220 -0
  253. data/lib/etassia/notation/plantuml/activity/route.rb +22 -0
  254. data/lib/etassia/notation/plantuml/activity/router.rb +308 -0
  255. data/lib/etassia/notation/plantuml/activity/row_gaps.rb +105 -0
  256. data/lib/etassia/notation/plantuml/activity/row_spread.rb +59 -0
  257. data/lib/etassia/notation/plantuml/activity/scene.rb +20 -0
  258. data/lib/etassia/notation/plantuml/activity/text.rb +36 -0
  259. data/lib/etassia/notation/plantuml/activity.rb +50 -0
  260. data/lib/etassia/notation/plantuml/arrow.rb +135 -0
  261. data/lib/etassia/notation/plantuml/caption.rb +50 -0
  262. data/lib/etassia/notation/plantuml/caption_layout.rb +97 -0
  263. data/lib/etassia/notation/plantuml/caption_rows.rb +67 -0
  264. data/lib/etassia/notation/plantuml/class_header.rb +178 -0
  265. data/lib/etassia/notation/plantuml/class_name.rb +51 -0
  266. data/lib/etassia/notation/plantuml/component.rb +37 -0
  267. data/lib/etassia/notation/plantuml/creole.rb +278 -0
  268. data/lib/etassia/notation/plantuml/creole_icons.rb +33 -0
  269. data/lib/etassia/notation/plantuml/diagram.rb +60 -0
  270. data/lib/etassia/notation/plantuml/diagram_builder.rb +442 -0
  271. data/lib/etassia/notation/plantuml/diagram_name.rb +51 -0
  272. data/lib/etassia/notation/plantuml/directives.rb +33 -0
  273. data/lib/etassia/notation/plantuml/drawing/box.rb +28 -0
  274. data/lib/etassia/notation/plantuml/drawing/face.rb +39 -0
  275. data/lib/etassia/notation/plantuml/drawing/pen.rb +105 -0
  276. data/lib/etassia/notation/plantuml/drawing/renderer.rb +36 -0
  277. data/lib/etassia/notation/plantuml/drawing/scene.rb +28 -0
  278. data/lib/etassia/notation/plantuml/drawing.rb +19 -0
  279. data/lib/etassia/notation/plantuml/ebnf/blocks.rb +272 -0
  280. data/lib/etassia/notation/plantuml/ebnf/layout.rb +117 -0
  281. data/lib/etassia/notation/plantuml/ebnf/model.rb +22 -0
  282. data/lib/etassia/notation/plantuml/ebnf/reader.rb +131 -0
  283. data/lib/etassia/notation/plantuml/ebnf.rb +34 -0
  284. data/lib/etassia/notation/plantuml/end_texts.rb +78 -0
  285. data/lib/etassia/notation/plantuml/entity.rb +54 -0
  286. data/lib/etassia/notation/plantuml/further.rb +38 -0
  287. data/lib/etassia/notation/plantuml/gantt/layout.rb +269 -0
  288. data/lib/etassia/notation/plantuml/gantt/reader.rb +210 -0
  289. data/lib/etassia/notation/plantuml/gantt/renderer.rb +81 -0
  290. data/lib/etassia/notation/plantuml/gantt.rb +34 -0
  291. data/lib/etassia/notation/plantuml/include/blocks.rb +66 -0
  292. data/lib/etassia/notation/plantuml/include/finder.rb +61 -0
  293. data/lib/etassia/notation/plantuml/include.rb +78 -0
  294. data/lib/etassia/notation/plantuml/ir_adapter/annotations.rb +40 -0
  295. data/lib/etassia/notation/plantuml/ir_adapter/links.rb +91 -0
  296. data/lib/etassia/notation/plantuml/ir_adapter/sink.rb +49 -0
  297. data/lib/etassia/notation/plantuml/ir_adapter/structure.rb +86 -0
  298. data/lib/etassia/notation/plantuml/ir_adapter.rb +32 -0
  299. data/lib/etassia/notation/plantuml/ir_reader/annotations.rb +51 -0
  300. data/lib/etassia/notation/plantuml/ir_reader/index.rb +49 -0
  301. data/lib/etassia/notation/plantuml/ir_reader/links.rb +133 -0
  302. data/lib/etassia/notation/plantuml/ir_reader/structure.rb +83 -0
  303. data/lib/etassia/notation/plantuml/ir_reader.rb +42 -0
  304. data/lib/etassia/notation/plantuml/junction.rb +20 -0
  305. data/lib/etassia/notation/plantuml/klass.rb +41 -0
  306. data/lib/etassia/notation/plantuml/label_clearance.rb +67 -0
  307. data/lib/etassia/notation/plantuml/layout.rb +2806 -0
  308. data/lib/etassia/notation/plantuml/member.rb +37 -0
  309. data/lib/etassia/notation/plantuml/member_icon.rb +87 -0
  310. data/lib/etassia/notation/plantuml/member_icon_elements.rb +91 -0
  311. data/lib/etassia/notation/plantuml/member_pitch.rb +76 -0
  312. data/lib/etassia/notation/plantuml/member_rule.rb +48 -0
  313. data/lib/etassia/notation/plantuml/network/grid.rb +170 -0
  314. data/lib/etassia/notation/plantuml/network/layout.rb +158 -0
  315. data/lib/etassia/notation/plantuml/network/model.rb +22 -0
  316. data/lib/etassia/notation/plantuml/network/placement.rb +64 -0
  317. data/lib/etassia/notation/plantuml/network/reader.rb +126 -0
  318. data/lib/etassia/notation/plantuml/network/shapes.rb +109 -0
  319. data/lib/etassia/notation/plantuml/network.rb +37 -0
  320. data/lib/etassia/notation/plantuml/node_graph/archimate.rb +49 -0
  321. data/lib/etassia/notation/plantuml/node_graph/box_reader.rb +86 -0
  322. data/lib/etassia/notation/plantuml/node_graph/cloud.rb +73 -0
  323. data/lib/etassia/notation/plantuml/node_graph/crows_foot.rb +85 -0
  324. data/lib/etassia/notation/plantuml/node_graph/curve.rb +66 -0
  325. data/lib/etassia/notation/plantuml/node_graph/data_reader.rb +77 -0
  326. data/lib/etassia/notation/plantuml/node_graph/data_shape.rb +39 -0
  327. data/lib/etassia/notation/plantuml/node_graph/detour.rb +98 -0
  328. data/lib/etassia/notation/plantuml/node_graph/edge.rb +26 -0
  329. data/lib/etassia/notation/plantuml/node_graph/entity_metrics.rb +49 -0
  330. data/lib/etassia/notation/plantuml/node_graph/entity_reader.rb +65 -0
  331. data/lib/etassia/notation/plantuml/node_graph/entity_shape.rb +111 -0
  332. data/lib/etassia/notation/plantuml/node_graph/figures.rb +92 -0
  333. data/lib/etassia/notation/plantuml/node_graph/frame_ports.rb +73 -0
  334. data/lib/etassia/notation/plantuml/node_graph/graph.rb +40 -0
  335. data/lib/etassia/notation/plantuml/node_graph/heads.rb +89 -0
  336. data/lib/etassia/notation/plantuml/node_graph/label_fan.rb +76 -0
  337. data/lib/etassia/notation/plantuml/node_graph/label_placer.rb +310 -0
  338. data/lib/etassia/notation/plantuml/node_graph/label_waypoint.rb +47 -0
  339. data/lib/etassia/notation/plantuml/node_graph/layout.rb +293 -0
  340. data/lib/etassia/notation/plantuml/node_graph/link.rb +78 -0
  341. data/lib/etassia/notation/plantuml/node_graph/macros.rb +39 -0
  342. data/lib/etassia/notation/plantuml/node_graph/metrics.rb +113 -0
  343. data/lib/etassia/notation/plantuml/node_graph/node.rb +44 -0
  344. data/lib/etassia/notation/plantuml/node_graph/note_tail.rb +58 -0
  345. data/lib/etassia/notation/plantuml/node_graph/packer.rb +126 -0
  346. data/lib/etassia/notation/plantuml/node_graph/parser.rb +309 -0
  347. data/lib/etassia/notation/plantuml/node_graph/paths.rb +48 -0
  348. data/lib/etassia/notation/plantuml/node_graph/ranking.rb +231 -0
  349. data/lib/etassia/notation/plantuml/node_graph/reader.rb +53 -0
  350. data/lib/etassia/notation/plantuml/node_graph/references.rb +38 -0
  351. data/lib/etassia/notation/plantuml/node_graph/renderer.rb +160 -0
  352. data/lib/etassia/notation/plantuml/node_graph/scene.rb +22 -0
  353. data/lib/etassia/notation/plantuml/node_graph/shapes.rb +302 -0
  354. data/lib/etassia/notation/plantuml/node_graph/skipped_ranks.rb +61 -0
  355. data/lib/etassia/notation/plantuml/node_graph/tab.rb +68 -0
  356. data/lib/etassia/notation/plantuml/node_graph/usecase_ellipse.rb +100 -0
  357. data/lib/etassia/notation/plantuml/node_graph.rb +38 -0
  358. data/lib/etassia/notation/plantuml/note.rb +27 -0
  359. data/lib/etassia/notation/plantuml/note_layout.rb +257 -0
  360. data/lib/etassia/notation/plantuml/note_outline.rb +68 -0
  361. data/lib/etassia/notation/plantuml/objects/map_rows.rb +46 -0
  362. data/lib/etassia/notation/plantuml/objects/reader.rb +208 -0
  363. data/lib/etassia/notation/plantuml/objects.rb +39 -0
  364. data/lib/etassia/notation/plantuml/package.rb +45 -0
  365. data/lib/etassia/notation/plantuml/package_color.rb +32 -0
  366. data/lib/etassia/notation/plantuml/package_frames.rb +178 -0
  367. data/lib/etassia/notation/plantuml/parser.rb +622 -0
  368. data/lib/etassia/notation/plantuml/portion_visibility.rb +91 -0
  369. data/lib/etassia/notation/plantuml/qualified_association_layout.rb +332 -0
  370. data/lib/etassia/notation/plantuml/qualifier_box.rb +78 -0
  371. data/lib/etassia/notation/plantuml/rank_chain.rb +527 -0
  372. data/lib/etassia/notation/plantuml/rank_hierarchy.rb +347 -0
  373. data/lib/etassia/notation/plantuml/relation.rb +103 -0
  374. data/lib/etassia/notation/plantuml/relation_label_separator.rb +199 -0
  375. data/lib/etassia/notation/plantuml/relation_path_points.rb +84 -0
  376. data/lib/etassia/notation/plantuml/renderer.rb +490 -0
  377. data/lib/etassia/notation/plantuml/scene.rb +134 -0
  378. data/lib/etassia/notation/plantuml/segment_box.rb +85 -0
  379. data/lib/etassia/notation/plantuml/sequence/activation.rb +28 -0
  380. data/lib/etassia/notation/plantuml/sequence/appearance.rb +71 -0
  381. data/lib/etassia/notation/plantuml/sequence/arrow_end.rb +23 -0
  382. data/lib/etassia/notation/plantuml/sequence/arrow_marks.rb +111 -0
  383. data/lib/etassia/notation/plantuml/sequence/arrow_style.rb +42 -0
  384. data/lib/etassia/notation/plantuml/sequence/arrow_syntax.rb +122 -0
  385. data/lib/etassia/notation/plantuml/sequence/bar_tracker.rb +59 -0
  386. data/lib/etassia/notation/plantuml/sequence/box.rb +23 -0
  387. data/lib/etassia/notation/plantuml/sequence/chrome.rb +60 -0
  388. data/lib/etassia/notation/plantuml/sequence/chrome_rows.rb +184 -0
  389. data/lib/etassia/notation/plantuml/sequence/create.rb +34 -0
  390. data/lib/etassia/notation/plantuml/sequence/delay.rb +19 -0
  391. data/lib/etassia/notation/plantuml/sequence/destroy.rb +20 -0
  392. data/lib/etassia/notation/plantuml/sequence/diagram.rb +86 -0
  393. data/lib/etassia/notation/plantuml/sequence/divider.rb +19 -0
  394. data/lib/etassia/notation/plantuml/sequence/edge.rb +53 -0
  395. data/lib/etassia/notation/plantuml/sequence/embedded.rb +77 -0
  396. data/lib/etassia/notation/plantuml/sequence/fill.rb +56 -0
  397. data/lib/etassia/notation/plantuml/sequence/fragment.rb +28 -0
  398. data/lib/etassia/notation/plantuml/sequence/fragment_shape.rb +138 -0
  399. data/lib/etassia/notation/plantuml/sequence/head_style.rb +43 -0
  400. data/lib/etassia/notation/plantuml/sequence/ir_adapter/blocks.rb +73 -0
  401. data/lib/etassia/notation/plantuml/sequence/ir_adapter/fills.rb +24 -0
  402. data/lib/etassia/notation/plantuml/sequence/ir_adapter/items.rb +48 -0
  403. data/lib/etassia/notation/plantuml/sequence/ir_adapter/messages.rb +62 -0
  404. data/lib/etassia/notation/plantuml/sequence/ir_adapter/participants.rb +42 -0
  405. data/lib/etassia/notation/plantuml/sequence/ir_adapter/settings.rb +54 -0
  406. data/lib/etassia/notation/plantuml/sequence/ir_adapter.rb +38 -0
  407. data/lib/etassia/notation/plantuml/sequence/ir_reader/blocks.rb +77 -0
  408. data/lib/etassia/notation/plantuml/sequence/ir_reader/fills.rb +27 -0
  409. data/lib/etassia/notation/plantuml/sequence/ir_reader/index.rb +36 -0
  410. data/lib/etassia/notation/plantuml/sequence/ir_reader/items.rb +35 -0
  411. data/lib/etassia/notation/plantuml/sequence/ir_reader/messages.rb +70 -0
  412. data/lib/etassia/notation/plantuml/sequence/ir_reader/participants.rb +42 -0
  413. data/lib/etassia/notation/plantuml/sequence/ir_reader/settings.rb +74 -0
  414. data/lib/etassia/notation/plantuml/sequence/ir_reader.rb +38 -0
  415. data/lib/etassia/notation/plantuml/sequence/keyword_defaults.rb +28 -0
  416. data/lib/etassia/notation/plantuml/sequence/layout.rb +1567 -0
  417. data/lib/etassia/notation/plantuml/sequence/message.rb +97 -0
  418. data/lib/etassia/notation/plantuml/sequence/message_wrap.rb +77 -0
  419. data/lib/etassia/notation/plantuml/sequence/note.rb +61 -0
  420. data/lib/etassia/notation/plantuml/sequence/note_fill.rb +35 -0
  421. data/lib/etassia/notation/plantuml/sequence/note_geometry.rb +126 -0
  422. data/lib/etassia/notation/plantuml/sequence/note_shape.rb +51 -0
  423. data/lib/etassia/notation/plantuml/sequence/numbered_label.rb +57 -0
  424. data/lib/etassia/notation/plantuml/sequence/numbering.rb +49 -0
  425. data/lib/etassia/notation/plantuml/sequence/outline.rb +204 -0
  426. data/lib/etassia/notation/plantuml/sequence/page_break.rb +17 -0
  427. data/lib/etassia/notation/plantuml/sequence/paint.rb +79 -0
  428. data/lib/etassia/notation/plantuml/sequence/parallel_message.rb +19 -0
  429. data/lib/etassia/notation/plantuml/sequence/parser.rb +844 -0
  430. data/lib/etassia/notation/plantuml/sequence/participant.rb +27 -0
  431. data/lib/etassia/notation/plantuml/sequence/picture.rb +29 -0
  432. data/lib/etassia/notation/plantuml/sequence/ref.rb +24 -0
  433. data/lib/etassia/notation/plantuml/sequence/ref_shape.rb +74 -0
  434. data/lib/etassia/notation/plantuml/sequence/refusals.rb +47 -0
  435. data/lib/etassia/notation/plantuml/sequence/renderer.rb +501 -0
  436. data/lib/etassia/notation/plantuml/sequence/scene.rb +200 -0
  437. data/lib/etassia/notation/plantuml/sequence/skin.rb +76 -0
  438. data/lib/etassia/notation/plantuml/sequence/spacing.rb +28 -0
  439. data/lib/etassia/notation/plantuml/sequence/style.rb +151 -0
  440. data/lib/etassia/notation/plantuml/sequence/tab_size.rb +42 -0
  441. data/lib/etassia/notation/plantuml/sequence/walker.rb +1190 -0
  442. data/lib/etassia/notation/plantuml/sequence.rb +47 -0
  443. data/lib/etassia/notation/plantuml/source_lines.rb +33 -0
  444. data/lib/etassia/notation/plantuml/state/axis_solver.rb +272 -0
  445. data/lib/etassia/notation/plantuml/state/cluster_padding.rb +75 -0
  446. data/lib/etassia/notation/plantuml/state/curve.rb +73 -0
  447. data/lib/etassia/notation/plantuml/state/dot_axis.rb +248 -0
  448. data/lib/etassia/notation/plantuml/state/extent.rb +42 -0
  449. data/lib/etassia/notation/plantuml/state/hierarchy.rb +355 -0
  450. data/lib/etassia/notation/plantuml/state/label_clearance.rb +148 -0
  451. data/lib/etassia/notation/plantuml/state/label_reach.rb +139 -0
  452. data/lib/etassia/notation/plantuml/state/layout.rb +173 -0
  453. data/lib/etassia/notation/plantuml/state/level_edges.rb +113 -0
  454. data/lib/etassia/notation/plantuml/state/look.rb +36 -0
  455. data/lib/etassia/notation/plantuml/state/note_shape.rb +73 -0
  456. data/lib/etassia/notation/plantuml/state/plane.rb +85 -0
  457. data/lib/etassia/notation/plantuml/state/ranking.rb +181 -0
  458. data/lib/etassia/notation/plantuml/state/reader.rb +310 -0
  459. data/lib/etassia/notation/plantuml/state/renderer.rb +253 -0
  460. data/lib/etassia/notation/plantuml/state/route_detour.rb +115 -0
  461. data/lib/etassia/notation/plantuml/state/route_geometry.rb +186 -0
  462. data/lib/etassia/notation/plantuml/state/routes.rb +196 -0
  463. data/lib/etassia/notation/plantuml/state/row.rb +88 -0
  464. data/lib/etassia/notation/plantuml/state/rows.rb +50 -0
  465. data/lib/etassia/notation/plantuml/state/stretch.rb +71 -0
  466. data/lib/etassia/notation/plantuml/state.rb +62 -0
  467. data/lib/etassia/notation/plantuml/stereotype_head.rb +58 -0
  468. data/lib/etassia/notation/plantuml/style/bound.rb +34 -0
  469. data/lib/etassia/notation/plantuml/style/colour.rb +41 -0
  470. data/lib/etassia/notation/plantuml/style/colour_names.rb +89 -0
  471. data/lib/etassia/notation/plantuml/style/defaults.rb +33 -0
  472. data/lib/etassia/notation/plantuml/style/inline.rb +56 -0
  473. data/lib/etassia/notation/plantuml/style/reader.rb +242 -0
  474. data/lib/etassia/notation/plantuml/style/sheet.rb +103 -0
  475. data/lib/etassia/notation/plantuml/style.rb +32 -0
  476. data/lib/etassia/notation/plantuml/style_reader.rb +160 -0
  477. data/lib/etassia/notation/plantuml/style_sheet.rb +27 -0
  478. data/lib/etassia/notation/plantuml/styled_relation_curve.rb +71 -0
  479. data/lib/etassia/notation/plantuml/timing/arrows.rb +81 -0
  480. data/lib/etassia/notation/plantuml/timing/axis.rb +100 -0
  481. data/lib/etassia/notation/plantuml/timing/layout.rb +172 -0
  482. data/lib/etassia/notation/plantuml/timing/model.rb +34 -0
  483. data/lib/etassia/notation/plantuml/timing/reader.rb +173 -0
  484. data/lib/etassia/notation/plantuml/timing/signals.rb +328 -0
  485. data/lib/etassia/notation/plantuml/timing.rb +38 -0
  486. data/lib/etassia/notation/plantuml/tree/layout.rb +276 -0
  487. data/lib/etassia/notation/plantuml/tree/mindmap_layout.rb +157 -0
  488. data/lib/etassia/notation/plantuml/tree/reader.rb +186 -0
  489. data/lib/etassia/notation/plantuml/tree/renderer.rb +67 -0
  490. data/lib/etassia/notation/plantuml/tree/role.rb +57 -0
  491. data/lib/etassia/notation/plantuml/tree/rules.rb +156 -0
  492. data/lib/etassia/notation/plantuml/tree/wbs_layout.rb +164 -0
  493. data/lib/etassia/notation/plantuml/tree.rb +41 -0
  494. data/lib/etassia/notation/plantuml/unsupported_construct_error.rb +39 -0
  495. data/lib/etassia/notation/plantuml/unsupported_constructs.rb +103 -0
  496. data/lib/etassia/notation/plantuml/usecase.rb +36 -0
  497. data/lib/etassia/notation/plantuml.rb +132 -0
  498. data/lib/etassia/notation/plantuml_data/boxes/frames.rb +101 -0
  499. data/lib/etassia/notation/plantuml_data/boxes/graph_layout.rb +348 -0
  500. data/lib/etassia/notation/plantuml_data/boxes/heads.rb +65 -0
  501. data/lib/etassia/notation/plantuml_data/boxes/highlights.rb +70 -0
  502. data/lib/etassia/notation/plantuml_data/boxes/inline_rows.rb +99 -0
  503. data/lib/etassia/notation/plantuml_data/boxes/json_reader.rb +24 -0
  504. data/lib/etassia/notation/plantuml_data/boxes/link_text.rb +94 -0
  505. data/lib/etassia/notation/plantuml_data/boxes/marks.rb +76 -0
  506. data/lib/etassia/notation/plantuml_data/boxes/ornaments.rb +130 -0
  507. data/lib/etassia/notation/plantuml_data/boxes/renderer.rb +95 -0
  508. data/lib/etassia/notation/plantuml_data/boxes/row_alignment.rb +58 -0
  509. data/lib/etassia/notation/plantuml_data/boxes/row_bend.rb +43 -0
  510. data/lib/etassia/notation/plantuml_data/boxes/scene.rb +27 -0
  511. data/lib/etassia/notation/plantuml_data/boxes/table.rb +56 -0
  512. data/lib/etassia/notation/plantuml_data/boxes/table_box.rb +355 -0
  513. data/lib/etassia/notation/plantuml_data/boxes/text.rb +101 -0
  514. data/lib/etassia/notation/plantuml_data/boxes/tree_builder.rb +82 -0
  515. data/lib/etassia/notation/plantuml_data/boxes/tree_layout.rb +82 -0
  516. data/lib/etassia/notation/plantuml_data/boxes/yaml_reader.rb +105 -0
  517. data/lib/etassia/notation/plantuml_data/openiconic.LICENSE +6 -0
  518. data/lib/etassia/notation/plantuml_data/openiconic.json +1 -0
  519. data/lib/etassia/notation/plantuml_data.rb +84 -0
  520. data/lib/etassia/notation/plantuml_salt/block.rb +80 -0
  521. data/lib/etassia/notation/plantuml_salt/box.rb +30 -0
  522. data/lib/etassia/notation/plantuml_salt/font_metrics.rb +28 -0
  523. data/lib/etassia/notation/plantuml_salt/grid_marks.rb +66 -0
  524. data/lib/etassia/notation/plantuml_salt/group_marks.rb +77 -0
  525. data/lib/etassia/notation/plantuml_salt/layout.rb +206 -0
  526. data/lib/etassia/notation/plantuml_salt/mark.rb +36 -0
  527. data/lib/etassia/notation/plantuml_salt/markup.rb +57 -0
  528. data/lib/etassia/notation/plantuml_salt/parser.rb +239 -0
  529. data/lib/etassia/notation/plantuml_salt/renderer.rb +138 -0
  530. data/lib/etassia/notation/plantuml_salt/row.rb +19 -0
  531. data/lib/etassia/notation/plantuml_salt/run.rb +34 -0
  532. data/lib/etassia/notation/plantuml_salt/scene.rb +23 -0
  533. data/lib/etassia/notation/plantuml_salt/scroll_marks.rb +94 -0
  534. data/lib/etassia/notation/plantuml_salt/tab_marks.rb +74 -0
  535. data/lib/etassia/notation/plantuml_salt/tree_marks.rb +127 -0
  536. data/lib/etassia/notation/plantuml_salt/widget.rb +40 -0
  537. data/lib/etassia/notation/plantuml_salt/widget_marks.rb +319 -0
  538. data/lib/etassia/notation/plantuml_salt.rb +66 -0
  539. data/lib/etassia/notation/plugin_failure.rb +42 -0
  540. data/lib/etassia/notation.rb +289 -0
  541. data/lib/etassia/parser/architecture.rb +25 -0
  542. data/lib/etassia/parser/atoms/delimited_run.rb +82 -0
  543. data/lib/etassia/parser/atoms/greedy_run.rb +116 -0
  544. data/lib/etassia/parser/atoms/joined.rb +47 -0
  545. data/lib/etassia/parser/atoms/trimmed_run.rb +47 -0
  546. data/lib/etassia/parser/base.rb +258 -0
  547. data/lib/etassia/parser/block.rb +32 -0
  548. data/lib/etassia/parser/builders/architecture.rb +135 -0
  549. data/lib/etassia/parser/builders/block.rb +288 -0
  550. data/lib/etassia/parser/builders/c4.rb +275 -0
  551. data/lib/etassia/parser/builders/capture_string.rb +17 -0
  552. data/lib/etassia/parser/builders/class_diagram.rb +539 -0
  553. data/lib/etassia/parser/builders/er_diagram.rb +308 -0
  554. data/lib/etassia/parser/builders/error.rb +51 -0
  555. data/lib/etassia/parser/builders/flowchart.rb +1150 -0
  556. data/lib/etassia/parser/builders/gantt.rb +267 -0
  557. data/lib/etassia/parser/builders/git_graph.rb +301 -0
  558. data/lib/etassia/parser/builders/info.rb +57 -0
  559. data/lib/etassia/parser/builders/kanban.rb +411 -0
  560. data/lib/etassia/parser/builders/mindmap.rb +271 -0
  561. data/lib/etassia/parser/builders/packet.rb +85 -0
  562. data/lib/etassia/parser/builders/pie.rb +105 -0
  563. data/lib/etassia/parser/builders/quadrant.rb +165 -0
  564. data/lib/etassia/parser/builders/radar.rb +157 -0
  565. data/lib/etassia/parser/builders/requirement.rb +230 -0
  566. data/lib/etassia/parser/builders/sankey.rb +142 -0
  567. data/lib/etassia/parser/builders/sequence.rb +431 -0
  568. data/lib/etassia/parser/builders/sequence_blocks.rb +117 -0
  569. data/lib/etassia/parser/builders/sequence_box_title.rb +57 -0
  570. data/lib/etassia/parser/builders/state_diagram.rb +364 -0
  571. data/lib/etassia/parser/builders/timeline.rb +133 -0
  572. data/lib/etassia/parser/builders/treemap.rb +138 -0
  573. data/lib/etassia/parser/builders/xy_chart.rb +148 -0
  574. data/lib/etassia/parser/c4.rb +37 -0
  575. data/lib/etassia/parser/class_diagram.rb +58 -0
  576. data/lib/etassia/parser/class_namespaces.rb +62 -0
  577. data/lib/etassia/parser/class_notes.rb +54 -0
  578. data/lib/etassia/parser/er_diagram.rb +28 -0
  579. data/lib/etassia/parser/error.rb +30 -0
  580. data/lib/etassia/parser/flowchart.rb +87 -0
  581. data/lib/etassia/parser/gantt.rb +43 -0
  582. data/lib/etassia/parser/git_graph.rb +106 -0
  583. data/lib/etassia/parser/grammars/architecture.rb +165 -0
  584. data/lib/etassia/parser/grammars/block.rb +190 -0
  585. data/lib/etassia/parser/grammars/c4.rb +229 -0
  586. data/lib/etassia/parser/grammars/class_diagram.rb +606 -0
  587. data/lib/etassia/parser/grammars/common.rb +107 -0
  588. data/lib/etassia/parser/grammars/er_diagram.rb +232 -0
  589. data/lib/etassia/parser/grammars/error.rb +32 -0
  590. data/lib/etassia/parser/grammars/flowchart.rb +1722 -0
  591. data/lib/etassia/parser/grammars/gantt.rb +198 -0
  592. data/lib/etassia/parser/grammars/git_graph.rb +234 -0
  593. data/lib/etassia/parser/grammars/info.rb +53 -0
  594. data/lib/etassia/parser/grammars/kanban.rb +417 -0
  595. data/lib/etassia/parser/grammars/mermaid_unicode_text.rb +108 -0
  596. data/lib/etassia/parser/grammars/mindmap.rb +220 -0
  597. data/lib/etassia/parser/grammars/packet.rb +71 -0
  598. data/lib/etassia/parser/grammars/pie.rb +128 -0
  599. data/lib/etassia/parser/grammars/quadrant.rb +202 -0
  600. data/lib/etassia/parser/grammars/radar.rb +158 -0
  601. data/lib/etassia/parser/grammars/requirement.rb +258 -0
  602. data/lib/etassia/parser/grammars/sankey.rb +104 -0
  603. data/lib/etassia/parser/grammars/sequence.rb +940 -0
  604. data/lib/etassia/parser/grammars/state_diagram.rb +382 -0
  605. data/lib/etassia/parser/grammars/timeline.rb +153 -0
  606. data/lib/etassia/parser/grammars/treemap.rb +122 -0
  607. data/lib/etassia/parser/grammars/xy_chart.rb +119 -0
  608. data/lib/etassia/parser/info.rb +32 -0
  609. data/lib/etassia/parser/kanban.rb +131 -0
  610. data/lib/etassia/parser/mermaid_shapes.rb +106 -0
  611. data/lib/etassia/parser/metadata_yaml.rb +452 -0
  612. data/lib/etassia/parser/mindmap.rb +92 -0
  613. data/lib/etassia/parser/native/builders/architecture.rb +103 -0
  614. data/lib/etassia/parser/native/builders/block.rb +68 -0
  615. data/lib/etassia/parser/native/builders/c4.rb +16 -0
  616. data/lib/etassia/parser/native/builders/class_diagram.rb +63 -0
  617. data/lib/etassia/parser/native/builders/er_diagram.rb +47 -0
  618. data/lib/etassia/parser/native/builders/flowchart.rb +58 -0
  619. data/lib/etassia/parser/native/builders/gantt.rb +35 -0
  620. data/lib/etassia/parser/native/builders/gitgraph.rb +35 -0
  621. data/lib/etassia/parser/native/builders/info.rb +30 -0
  622. data/lib/etassia/parser/native/builders/kanban.rb +37 -0
  623. data/lib/etassia/parser/native/builders/mindmap.rb +26 -0
  624. data/lib/etassia/parser/native/builders/packet.rb +63 -0
  625. data/lib/etassia/parser/native/builders/pie.rb +82 -0
  626. data/lib/etassia/parser/native/builders/quadrant.rb +27 -0
  627. data/lib/etassia/parser/native/builders/radar.rb +84 -0
  628. data/lib/etassia/parser/native/builders/requirement.rb +25 -0
  629. data/lib/etassia/parser/native/builders/sankey.rb +18 -0
  630. data/lib/etassia/parser/native/builders/sequence.rb +48 -0
  631. data/lib/etassia/parser/native/builders/state_diagram.rb +61 -0
  632. data/lib/etassia/parser/native/builders/timeline.rb +46 -0
  633. data/lib/etassia/parser/native/builders/treemap.rb +60 -0
  634. data/lib/etassia/parser/native/builders/user_journey.rb +83 -0
  635. data/lib/etassia/parser/native/builders/xy_chart.rb +94 -0
  636. data/lib/etassia/parser/native/declined.rb +12 -0
  637. data/lib/etassia/parser/native/mermaid.json +1 -0
  638. data/lib/etassia/parser/native/text.rb +31 -0
  639. data/lib/etassia/parser/native.rb +99 -0
  640. data/lib/etassia/parser/packet.rb +56 -0
  641. data/lib/etassia/parser/pie.rb +32 -0
  642. data/lib/etassia/parser/quadrant.rb +77 -0
  643. data/lib/etassia/parser/radar.rb +99 -0
  644. data/lib/etassia/parser/requirement.rb +32 -0
  645. data/lib/etassia/parser/sankey.rb +47 -0
  646. data/lib/etassia/parser/sequence.rb +34 -0
  647. data/lib/etassia/parser/state_diagram.rb +29 -0
  648. data/lib/etassia/parser/timeline.rb +40 -0
  649. data/lib/etassia/parser/treemap.rb +25 -0
  650. data/lib/etassia/parser/user_journey.rb +253 -0
  651. data/lib/etassia/parser/xy_chart.rb +98 -0
  652. data/lib/etassia/parser.rb +18 -0
  653. data/lib/etassia/renderer/architecture.rb +232 -0
  654. data/lib/etassia/renderer/architecture_edge_router.rb +379 -0
  655. data/lib/etassia/renderer/base.rb +295 -0
  656. data/lib/etassia/renderer/block.rb +157 -0
  657. data/lib/etassia/renderer/c4.rb +294 -0
  658. data/lib/etassia/renderer/class_diagram.rb +671 -0
  659. data/lib/etassia/renderer/er_diagram.rb +842 -0
  660. data/lib/etassia/renderer/error.rb +131 -0
  661. data/lib/etassia/renderer/flowchart.rb +315 -0
  662. data/lib/etassia/renderer/gantt.rb +177 -0
  663. data/lib/etassia/renderer/git_graph.rb +224 -0
  664. data/lib/etassia/renderer/info.rb +59 -0
  665. data/lib/etassia/renderer/kanban.rb +157 -0
  666. data/lib/etassia/renderer/line_break_text.rb +56 -0
  667. data/lib/etassia/renderer/markdown_text.rb +106 -0
  668. data/lib/etassia/renderer/mindmap.rb +290 -0
  669. data/lib/etassia/renderer/packet.rb +87 -0
  670. data/lib/etassia/renderer/pie.rb +113 -0
  671. data/lib/etassia/renderer/quadrant.rb +189 -0
  672. data/lib/etassia/renderer/radar.rb +191 -0
  673. data/lib/etassia/renderer/requirement.rb +209 -0
  674. data/lib/etassia/renderer/sankey.rb +114 -0
  675. data/lib/etassia/renderer/sequence/frame_drawing.rb +169 -0
  676. data/lib/etassia/renderer/sequence/note_drawing.rb +69 -0
  677. data/lib/etassia/renderer/sequence.rb +490 -0
  678. data/lib/etassia/renderer/state_diagram.rb +202 -0
  679. data/lib/etassia/renderer/timeline.rb +134 -0
  680. data/lib/etassia/renderer/timeline_palette.rb +18 -0
  681. data/lib/etassia/renderer/treemap/section_total.rb +33 -0
  682. data/lib/etassia/renderer/treemap.rb +128 -0
  683. data/lib/etassia/renderer/user_journey.rb +209 -0
  684. data/lib/etassia/renderer/xy_chart.rb +180 -0
  685. data/lib/etassia/renderer.rb +22 -0
  686. data/lib/etassia/runtime.rb +125 -0
  687. data/lib/etassia/source/frontmatter.rb +501 -0
  688. data/lib/etassia/source.rb +224 -0
  689. data/lib/etassia/svg/arrowhead.rb +205 -0
  690. data/lib/etassia/svg/circle.rb +19 -0
  691. data/lib/etassia/svg/document.rb +106 -0
  692. data/lib/etassia/svg/element.rb +209 -0
  693. data/lib/etassia/svg/ellipse.rb +23 -0
  694. data/lib/etassia/svg/escaping.rb +149 -0
  695. data/lib/etassia/svg/group.rb +64 -0
  696. data/lib/etassia/svg/line.rb +21 -0
  697. data/lib/etassia/svg/numbers.rb +53 -0
  698. data/lib/etassia/svg/path.rb +67 -0
  699. data/lib/etassia/svg/path_geometry.rb +378 -0
  700. data/lib/etassia/svg/polygon.rb +25 -0
  701. data/lib/etassia/svg/polyline.rb +28 -0
  702. data/lib/etassia/svg/rect.rb +34 -0
  703. data/lib/etassia/svg/style.rb +50 -0
  704. data/lib/etassia/svg/text.rb +181 -0
  705. data/lib/etassia/svg/tspan.rb +48 -0
  706. data/lib/etassia/svg.rb +38 -0
  707. data/lib/etassia/text_measurement/arial_advances.rb +122 -0
  708. data/lib/etassia/text_measurement/dejavu_advances.rb +111 -0
  709. data/lib/etassia/text_measurement/times_advances.rb +64 -0
  710. data/lib/etassia/text_measurement/trebuchet_advances.rb +53 -0
  711. data/lib/etassia/text_measurement/trebuchet_kerning.rb +29 -0
  712. data/lib/etassia/text_measurement.rb +198 -0
  713. data/lib/etassia/theme/builtin/dark.yml +229 -0
  714. data/lib/etassia/theme/builtin/default.yml +239 -0
  715. data/lib/etassia/theme/builtin/high_contrast.yml +231 -0
  716. data/lib/etassia/theme/builtin/light.yml +229 -0
  717. data/lib/etassia/theme/color_palette.rb +97 -0
  718. data/lib/etassia/theme/completion.rb +50 -0
  719. data/lib/etassia/theme/effect_styles.rb +34 -0
  720. data/lib/etassia/theme/registry.rb +45 -0
  721. data/lib/etassia/theme/shape_styles.rb +34 -0
  722. data/lib/etassia/theme/spacing_config.rb +30 -0
  723. data/lib/etassia/theme/typography.rb +40 -0
  724. data/lib/etassia/theme.rb +73 -0
  725. data/lib/etassia/version.rb +5 -0
  726. data/lib/etassia.rb +40 -0
  727. metadata +865 -0
@@ -0,0 +1,1190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "activation"
4
+ require_relative "appearance"
5
+ require_relative "message_wrap"
6
+ require_relative "tab_size"
7
+ require_relative "arrow_marks"
8
+ require_relative "bar_tracker"
9
+ require_relative "create"
10
+ require_relative "destroy"
11
+ require_relative "delay"
12
+ require_relative "divider"
13
+ require_relative "spacing"
14
+ require_relative "edge"
15
+ require_relative "fragment"
16
+ require_relative "message"
17
+ require_relative "note"
18
+ require_relative "fragment_shape"
19
+ require_relative "note_geometry"
20
+ require_relative "numbered_label"
21
+ require_relative "page_break"
22
+ require_relative "note_shape"
23
+ require_relative "ref"
24
+ require_relative "ref_shape"
25
+ require_relative "scene"
26
+
27
+ module Etassia
28
+ module Notation
29
+ module PlantUML
30
+ module Sequence
31
+ # Walks the items of a diagram down the page and places each one,
32
+ # given where the lifelines are. Rows are consumed from `y` downwards;
33
+ # `left`, `right` and `y` describe the space the items ended up using.
34
+ class Walker
35
+ ROW = 40.0
36
+ LINE_LEADING = NoteGeometry::LINE_LEADING
37
+ SELF_WIDTH = 42.0
38
+ CLASSIC_SELF_WIDTH = 42.0
39
+ CLASSIC_UNLABELLED_SELF_ROW = 27.0
40
+ CLASSIC_ROW_SPARE = 12.3105
41
+ CLASSIC_TAIL = 17.0
42
+ CLASSIC_GUARD_ROW = 13.0
43
+ CLASSIC_SELF_TAIL = 30.0
44
+ CLASSIC_LABEL_INSET = 7.0
45
+ CLASSIC_LABEL_RISE = 4.74
46
+ # A `...` gap is 28 high, and 40.95 with its 11-point text, whose
47
+ # baseline sits 24.63 below the gap's top (PlantUML 1.2026.6).
48
+ CLASSIC_DELAY_ROW = 28.0
49
+ CLASSIC_LABEL_ROW = 21.3105
50
+ CLASSIC_DELAY_LABEL_ROW = 40.9551
51
+ CLASSIC_DELAY_TEXT_DROP = 24.6348
52
+ private_constant :CLASSIC_DELAY_ROW, :CLASSIC_DELAY_LABEL_ROW,
53
+ :CLASSIC_DELAY_TEXT_DROP
54
+ private_constant :CLASSIC_SELF_WIDTH, :CLASSIC_UNLABELLED_SELF_ROW,
55
+ :CLASSIC_ROW_SPARE, :CLASSIC_TAIL,
56
+ :CLASSIC_SELF_TAIL, :CLASSIC_GUARD_ROW,
57
+ :CLASSIC_LABEL_RISE
58
+ SPARE_ROWS = [Destroy, Spacing, Delay].freeze
59
+ private_constant :SPARE_ROWS
60
+ SELF_HEIGHT = 20.0
61
+ COMPACT_FRAGMENT_SELF_HEIGHT = 17.75
62
+ LOOP_HEIGHT = 13.0
63
+ # PlantUML 1.2026.6 ends the bar of a lifeline one pixel below a
64
+ # loop written on it when the bar closes right after: the loop
65
+ # drops 5, and any bar closing after a loop ends 6 below its row.
66
+ CLOSING_LOOP_DROP = 5.0
67
+ CLOSING_LOOP_BAR_REACH = 6.0
68
+ private_constant :LOOP_HEIGHT, :CLOSING_LOOP_DROP,
69
+ :CLOSING_LOOP_BAR_REACH
70
+ TAB_HEIGHT = 20.0
71
+ CROSS_HALF = 9.0
72
+ ACTIVATION_END_INSET = 7.0
73
+ TOP_OFFSET = 20.0
74
+ NOTE_ABOVE = 35.0
75
+ # PlantUML 1.2026.6 leaves this much above and below a message row
76
+ # that carries a note.
77
+ ATTACHED_NOTE_PAD = 3.0
78
+ ATTACHED_NOTE_RISE = 5.6895
79
+ STANDALONE_NOTE_RISE = 16.3105
80
+ STANDALONE_NOTE_TAIL = 26.6211
81
+ private_constant :ATTACHED_NOTE_PAD, :ATTACHED_NOTE_RISE,
82
+ :STANDALONE_NOTE_RISE, :STANDALONE_NOTE_TAIL
83
+ LEFT_EDGE = 0.0
84
+ TEOZ_NESTED_HEADER_ROW = 31.3105
85
+ # What the row of a message that creates a participant gains over
86
+ # a plain one, measured against PlantUML 1.2026.6.
87
+ CREATION_EXTRA_ROW = 12.18
88
+ TEOZ_MESSAGE_HEADER_ROW = 28.6211
89
+ TEOZ_EMPTY_HEADER_ROW = 13.3105
90
+ TEOZ_MESSAGE_BASE_ROW = 14.0
91
+ TEOZ_FRAME_BOTTOM_PAD = 6.0
92
+ TEOZ_ROOT_CLOSE_GAP = 16.6895
93
+ TEOZ_NESTED_CLOSE_GAP = 14.0
94
+ TEOZ_LABEL_HEIGHT = 15.3105
95
+ TEOZ_SELF_CONTACT_INSET = 2.5
96
+ TEOZ_CONTACT_PADDING = 2.0
97
+ private_constant :CREATION_EXTRA_ROW, :TEOZ_NESTED_HEADER_ROW,
98
+ :TEOZ_MESSAGE_HEADER_ROW,
99
+ :TEOZ_EMPTY_HEADER_ROW, :TEOZ_MESSAGE_BASE_ROW,
100
+ :TEOZ_FRAME_BOTTOM_PAD,
101
+ :TEOZ_ROOT_CLOSE_GAP,
102
+ :TEOZ_NESTED_CLOSE_GAP, :TEOZ_LABEL_HEIGHT,
103
+ :TEOZ_SELF_CONTACT_INSET, :TEOZ_CONTACT_PADDING
104
+ PLACERS = { Message => :message, Activation => :activation,
105
+ Destroy => :destroy, Create => :create,
106
+ Fragment => :fragment,
107
+ Ref => :ref, PageBreak => :page_break,
108
+ Delay => :delay, Spacing => :spacing }.freeze
109
+ Config = Struct.new(:message_measure, :message_font_size,
110
+ :message_family, :label_inset, :row, :self_row,
111
+ :unlabelled_row, :following_unlabelled_row,
112
+ :fragment_row, :branch_gap,
113
+ :fragment_close_gap, :divider_row,
114
+ :group_fragment_row, :group_close_gap,
115
+ :activation_edges, :teoz_fragment_cadence,
116
+ :note_font_size,
117
+ :deactivation_gap, :bare_deactivation_gap,
118
+ :frame_top_offset, :frame_bottom_offset,
119
+ :branch_offset, :bounds, :head_halves,
120
+ keyword_init: true)
121
+
122
+ def self.following_activations(items, parallel_notes: false)
123
+ items.take_while do |item|
124
+ item.is_a?(Activation) ||
125
+ (parallel_notes && item.is_a?(Note) && item.parallel?)
126
+ end.grep(Activation)
127
+ end
128
+
129
+ attr_reader :arrows, :notes, :fragments, :dividers, :delays, :crosses,
130
+ :left, :right, :y, :page_breaks, :created
131
+
132
+ # @param lifelines [Hash{String => Float}] centre of each
133
+ # participant's lifeline, in participant order
134
+ # @param measure [#call] text width in pixels
135
+ # @param start_y [Float] where the first row goes
136
+ # @param config [Config] `bounds` is the left and right edge of the
137
+ # heads
138
+ def initialize(lifelines:, measure:, font_size:, start_y:,
139
+ config: Config.new)
140
+ @ids = lifelines.keys
141
+ @centers = lifelines.values
142
+ @measure = measure
143
+ @font_size = font_size
144
+ @y = start_y
145
+ apply_config(config)
146
+ start_empty
147
+ end
148
+
149
+ # @param edge_right [Float, nil] where a message written `->]` ends;
150
+ # required when the items have one
151
+ # @param appearance [Appearance] what the fragment tabs are drawn in
152
+ def run(items, edge_right: nil, appearance: Appearance.new)
153
+ @edge_right = edge_right
154
+ @appearance = appearance
155
+ @fragmented = items.any?(Fragment)
156
+ @creations = Create.messages(items)
157
+ @fragment_contacts = fragment_contacts(items)
158
+ walk_items(items)
159
+ @tracker.finish(@y - 20)
160
+ @y -= classic_tail_trim(items)
161
+ self
162
+ end
163
+
164
+ def bars
165
+ @tracker.bars
166
+ end
167
+
168
+ private
169
+
170
+ # The foot heads of a plain classic diagram sit CLASSIC_TAIL below
171
+ # the last message line (a self loop's top, CLASSIC_SELF_TAIL), not
172
+ # a whole row below it.
173
+ def classic_tail_trim(items)
174
+ return 0.0 if @label_inset || @teoz_fragment_cadence
175
+
176
+ last = items.reverse_each.find { |item| !item.is_a?(Activation) }
177
+ return 0.0 if last.nil? || bar_after?(items, last)
178
+
179
+ tail_trim(last)
180
+ end
181
+
182
+ def tail_trim(last)
183
+ return CLASSIC_ROW_SPARE if last.is_a?(Fragment)
184
+ return @spare_row if last.is_a?(Message)
185
+
186
+ SPARE_ROWS.include?(last.class) ? CLASSIC_ROW_SPARE : 0.0
187
+ end
188
+
189
+ # A bar opened and closed after the last message is not drawn the
190
+ # way PlantUML does, so its tail is left as it was.
191
+ def bar_after?(items, last)
192
+ trailing = items.drop(items.rindex { |item| item.equal?(last) } + 1)
193
+ trailing.any?(&:on?) && trailing.any? { |item| !item.on? }
194
+ end
195
+
196
+ def apply_config(config)
197
+ apply_message_config(config)
198
+ apply_cadence_config(config)
199
+ @bounds = config.bounds
200
+ @note_font_size = config_value(config, :note_font_size, @font_size)
201
+ @head_halves = config.head_halves || {}
202
+ end
203
+
204
+ def apply_message_config(config)
205
+ @message_measure = config_value(config, :message_measure, @measure)
206
+ @monospace_measure = @message_measure
207
+ @message_font_size = config_value(config, :message_font_size,
208
+ @font_size)
209
+ @message_family = config.message_family
210
+ @label_inset = config.label_inset
211
+ @teoz_fragment_cadence = config.teoz_fragment_cadence
212
+ @activation_edges = config.activation_edges
213
+ end
214
+
215
+ def apply_cadence_config(config)
216
+ @row = config_value(config, :row, ROW)
217
+ @self_row = config_value(config, :self_row, ROW * 1.5)
218
+ apply_fragment_cadence(config)
219
+ @divider_row = config_value(config, :divider_row, ROW)
220
+ @deactivation_gap = config_value(config, :deactivation_gap, 0.0)
221
+ @bare_deactivation_gap = config_value(
222
+ config, :bare_deactivation_gap, 0.0
223
+ )
224
+ end
225
+
226
+ def apply_fragment_cadence(config)
227
+ @unlabelled_row = config.unlabelled_row
228
+ @following_unlabelled_row = config.following_unlabelled_row
229
+ @fragment_row = config_value(config, :fragment_row, TAB_HEIGHT)
230
+ @branch_gap = config_value(config, :branch_gap, 28.0)
231
+ @fragment_close_gap = config_value(config, :fragment_close_gap,
232
+ 10.0)
233
+ @group_fragment_row = config.group_fragment_row
234
+ @group_close_gap = config.group_close_gap
235
+ apply_frame_offsets(config)
236
+ end
237
+
238
+ # How far above the row line a frame's top, bottom and `else`
239
+ # line sit.
240
+ def apply_frame_offsets(config)
241
+ @frame_top_offset = config_value(config, :frame_top_offset,
242
+ TOP_OFFSET)
243
+ @frame_bottom_offset = config_value(config, :frame_bottom_offset,
244
+ TOP_OFFSET)
245
+ @branch_offset = config_value(config, :branch_offset, 10.0)
246
+ end
247
+
248
+ def config_value(config, member, default)
249
+ config[member] || default
250
+ end
251
+
252
+ def walk_items(items)
253
+ previous = nil
254
+ items.each_with_index do |item, index|
255
+ prepare_item(items, index, item)
256
+ visit(item, previous)
257
+ previous = item unless item.is_a?(Activation)
258
+ end
259
+ end
260
+
261
+ def prepare_item(items, index, item)
262
+ following = items.drop(index + 1)
263
+ collect_following_activations(following, item)
264
+ @next_non_activation = following.find do |candidate|
265
+ !candidate.is_a?(Activation)
266
+ end
267
+ @mark_y = nil unless anchoring?(item)
268
+ @headroom = headroom(items, index)
269
+ @item_index = index
270
+ end
271
+
272
+ def collect_following_activations(following, item)
273
+ parallel_notes = @label_inset && item.is_a?(Message)
274
+ @following_activations = self.class.following_activations(
275
+ following, parallel_notes: parallel_notes
276
+ )
277
+ end
278
+
279
+ def start_empty
280
+ start_collections
281
+ @headroom = 0.0
282
+ @spare_row = 0.0
283
+ @tracker = BarTracker.new(method(:centre))
284
+ @blocks = []
285
+ @left = Float::INFINITY
286
+ @right = -Float::INFINITY
287
+ @created = {}
288
+ end
289
+
290
+ def start_collections
291
+ @arrows = []
292
+ @notes = []
293
+ @fragments = []
294
+ @dividers = []
295
+ @delays = []
296
+ @crosses = []
297
+ @page_breaks = []
298
+ end
299
+
300
+ def anchoring?(item)
301
+ [Message, Activation, Destroy].any? { |kind| item.is_a?(kind) }
302
+ end
303
+
304
+ # A parallel item starts on the row of the one before it, and the
305
+ # page continues below whichever of the two reaches further down.
306
+ def visit(item, previous)
307
+ @previous_self_message = false unless continues_self_run?(item)
308
+ @resume = nil
309
+ offset = row_offset(item)
310
+ begin_row(item, offset, previous) if offset
311
+ place(item, previous)
312
+ resume_after_parallel(item) if @resume && !opens_block?(item)
313
+ end
314
+
315
+ def resume_after_parallel(item)
316
+ @y = if parallel_side_note?(item)
317
+ @resume
318
+ else
319
+ [@y, @resume].max
320
+ end
321
+ end
322
+
323
+ def parallel_side_note?(item)
324
+ item.is_a?(Note) && !item.targets.empty? &&
325
+ %i[left right].include?(item.side)
326
+ end
327
+
328
+ # How far below its top edge an item's first line sits; nil for an
329
+ # item that does not take a row of its own.
330
+ def row_offset(item)
331
+ case item
332
+ when Message then 0.0
333
+ when Note then STANDALONE_NOTE_RISE
334
+ when Ref then TOP_OFFSET
335
+ when Fragment then TOP_OFFSET if item.phase == :open
336
+ end
337
+ end
338
+
339
+ def continues_self_run?(item)
340
+ item.is_a?(Message) || item.is_a?(Activation)
341
+ end
342
+
343
+ def opens_block?(item)
344
+ item.is_a?(Fragment) && item.phase == :open
345
+ end
346
+
347
+ # Records the line a following `&` item aligns its top with, or
348
+ # moves a `&` item up to the line the one before it recorded.
349
+ def begin_row(item, offset, previous)
350
+ if item.parallel?
351
+ @resume = @y
352
+ @y = @row_line + offset + parallel_contact_offset(item, previous)
353
+ else
354
+ attached = item.is_a?(Note) && item.attached?
355
+ @row_line = attached ? @last_y : @y - offset
356
+ end
357
+ end
358
+
359
+ # Parallel tiles share their contact point, rather than their top.
360
+ # A frame contacts at its top; a self arrow contacts below its shaft.
361
+ def parallel_contact_offset(item, previous)
362
+ return 0.0 unless @teoz_fragment_cadence
363
+ if self_before_frame?(item, previous)
364
+ return TEOZ_SELF_CONTACT_INSET
365
+ end
366
+ return 0.0 unless self_after_frame?(item, previous)
367
+
368
+ -TEOZ_SELF_CONTACT_INSET - (self_top - @y)
369
+ end
370
+
371
+ def self_before_frame?(item, previous)
372
+ item.is_a?(Fragment) && previous.is_a?(Message) &&
373
+ previous.self_message?
374
+ end
375
+
376
+ def self_after_frame?(item, previous)
377
+ item.is_a?(Message) && item.self_message? &&
378
+ previous.is_a?(Fragment) && previous.phase == :close
379
+ end
380
+
381
+ def place(item, previous)
382
+ return note(item, previous) if item.is_a?(Note)
383
+
384
+ handler = PLACERS.find { |type, _| item.is_a?(type) }
385
+ send(handler ? handler.last : :divider, item)
386
+ end
387
+
388
+ def wrap
389
+ MessageWrap.new(@appearance.max_message, @message_measure)
390
+ end
391
+
392
+ def message(message)
393
+ apply_message_font(message)
394
+ start_row(message)
395
+ @message_activations = active_participants
396
+ from, to = ends_of(message)
397
+ to = created_end(message, from, to)
398
+ draw(message, from, to) unless message.style.hidden
399
+ touch(*extent(message, from, to))
400
+ advance_by(message, message_row(message) + creation_extra(message))
401
+ end
402
+
403
+ def advance_by(message, row)
404
+ @spare_row = row - tail_of(message)
405
+ @y += row - @loop_drop
406
+ end
407
+
408
+ def closing_loop_drop(message)
409
+ return 0.0 unless classic? && message.self_message?
410
+
411
+ closes = @following_activations.any? do |activation|
412
+ !activation.on? && activation.participant == message.from
413
+ end
414
+ closes ? CLOSING_LOOP_DROP : 0.0
415
+ end
416
+
417
+ def classic?
418
+ !@label_inset && !@teoz_fragment_cadence
419
+ end
420
+
421
+ def tail_of(message)
422
+ message.self_message? ? CLASSIC_SELF_TAIL : CLASSIC_TAIL
423
+ end
424
+
425
+ # `create X` has no row of its own; the message to X lands on the
426
+ # left or right edge of X's head, which is drawn at this row.
427
+ def created_end(message, from, to)
428
+ id = @creations[message] or return to
429
+
430
+ @created[id] = @y
431
+ to - ((to <=> from) * @head_halves.fetch(id, 0.0))
432
+ end
433
+
434
+ def creation_extra(message)
435
+ @creations.key?(message) ? CREATION_EXTRA_ROW : 0.0
436
+ end
437
+
438
+ def create(_item)
439
+ nil
440
+ end
441
+
442
+ def apply_message_font(message)
443
+ return unless @label_inset
444
+
445
+ @message_family = message.label_family || "sans-serif"
446
+ @message_measure = if @message_family == "monospace"
447
+ @monospace_measure
448
+ else
449
+ method(:sans_message_width)
450
+ end
451
+ end
452
+
453
+ def sans_message_width(text)
454
+ NoteGeometry.widest_line(text, @message_font_size)
455
+ end
456
+
457
+ def message_row(message)
458
+ return teoz_message_row(message) if @teoz_fragment_cadence
459
+
460
+ return self_message_row(message) if message.self_message?
461
+
462
+ plain_message_row(message)
463
+ end
464
+
465
+ def plain_message_row(message)
466
+ if following_unlabelled_row?
467
+ return @following_unlabelled_row
468
+ end
469
+ if @unlabelled_row && message.label.to_s.empty?
470
+ return @unlabelled_row
471
+ end
472
+
473
+ if @label_inset && message.label_family != "monospace"
474
+ return 29.3105
475
+ end
476
+
477
+ @row
478
+ end
479
+
480
+ def teoz_message_row(message)
481
+ text_height = teoz_row_text_height(message)
482
+ loop_height = message.self_message? ? LOOP_HEIGHT : 0.0
483
+ TEOZ_MESSAGE_BASE_ROW + text_height + loop_height
484
+ end
485
+
486
+ def teoz_row_text_height(message)
487
+ following = @next_non_activation
488
+ if following.is_a?(Message) && !following.parallel?
489
+ return following.label.to_s.empty? ? 0.0 : TEOZ_LABEL_HEIGHT
490
+ end
491
+
492
+ teoz_text_height(message)
493
+ end
494
+
495
+ def teoz_text_height(message)
496
+ label_lines(message).size * TEOZ_LABEL_HEIGHT
497
+ end
498
+
499
+ # A classic loop leaves room for the label of the message that
500
+ # follows it, so the row is short only before an unlabelled one.
501
+ def self_message_row(message)
502
+ return @self_row if @label_inset
503
+
504
+ following = @next_non_activation
505
+ labelled = if following.is_a?(Message)
506
+ !following.label.to_s.empty?
507
+ else
508
+ !message.label.to_s.empty?
509
+ end
510
+ labelled ? @self_row : CLASSIC_UNLABELLED_SELF_ROW
511
+ end
512
+
513
+ def following_unlabelled_row?
514
+ @following_unlabelled_row && following_unlabelled_message?
515
+ end
516
+
517
+ def following_unlabelled_message?
518
+ @next_non_activation.is_a?(Message) &&
519
+ (!@label_inset || !@next_non_activation.self_message?) &&
520
+ @next_non_activation.label.to_s.empty?
521
+ end
522
+
523
+ def active_participants
524
+ @following_activations.select(&:on?).map(&:participant)
525
+ end
526
+
527
+ # A note hanging off a message reaches above it; a tall one pushes
528
+ # the message down instead of out over the heads.
529
+ def headroom(items, index)
530
+ return 0.0 unless items[index].is_a?(Message)
531
+
532
+ following = items.drop(index + 1).find do |other|
533
+ !other.is_a?(Activation)
534
+ end
535
+ return 0.0 unless following.is_a?(Note) && following.attached?
536
+
537
+ attached_note_pad(items[index], following) +
538
+ attached_note_headroom(following)
539
+ end
540
+
541
+ # Self arrows, parallel rows and picture notes carry their own
542
+ # spacing.
543
+ def attached_note_pad(item, note)
544
+ return 0.0 if @teoz_fragment_cadence || note.picture
545
+ return 0.0 if item.is_a?(Message) && own_spacing?(item)
546
+
547
+ ATTACHED_NOTE_PAD
548
+ end
549
+
550
+ def own_spacing?(message)
551
+ message.self_message? || message.parallel?
552
+ end
553
+
554
+ def attached_note_headroom(note)
555
+ height = NoteGeometry.height(note)
556
+ clearance = note.picture ? height / 2 : height
557
+ [clearance - NOTE_ABOVE, 0.0].max
558
+ end
559
+
560
+ def start_row(message)
561
+ @y += @headroom
562
+ @y += [label_lines(message).size - 1, 0].max * line_step
563
+ @self_drop = message.self_message? ? self_height : 0.0
564
+ @last_y = @mark_y = @y
565
+ @y += @loop_drop = closing_loop_drop(message)
566
+ record_text_height(message)
567
+ end
568
+
569
+ def record_text_height(message)
570
+ return if !@teoz_fragment_cadence || @blocks.empty?
571
+
572
+ @blocks.last[:text_height] = teoz_text_height(message)
573
+ end
574
+
575
+ def ends_of(message)
576
+ [message.from, message.to].map { |place| place_of(place, message) }
577
+ end
578
+
579
+ def draw(message, from, to)
580
+ @arrows << arrow_record(message, arrow_for(message, from, to))
581
+ end
582
+
583
+ def extent(message, from, to)
584
+ return [from - reach(message), from] if loops_left?(message)
585
+
586
+ low, high = reached(message, from, to).minmax
587
+ [low, high + (message.self_message? ? reach(message) : 0)]
588
+ end
589
+
590
+ # The diagram edge belongs to no row's extent: only the participant
591
+ # at the other end does.
592
+ def reached(message, from, to)
593
+ return [from, to] unless message.edge&.global?
594
+
595
+ [centre(message.participants.first)]
596
+ end
597
+
598
+ # Where an end of a message lands: a lifeline, or an edge, which a
599
+ # ring drawn there pulls inwards.
600
+ def place_of(place, message)
601
+ return participant_place(place, message) unless place.is_a?(Edge)
602
+
603
+ inward = place.left? ? 1 : -1
604
+ ring = message.edge_end.circle ? Edge::RING_INSET : 0.0
605
+ edge_x(place, message) + (inward * ring)
606
+ end
607
+
608
+ def participant_place(place, message)
609
+ at = centre(place)
610
+ if @label_inset && message.self_message?
611
+ return compact_self_place(place, message, at)
612
+ end
613
+ if @label_inset || @activation_edges
614
+ return compact_activation_place(place, message, at)
615
+ end
616
+ return at unless activated_end?(place, message)
617
+
618
+ at + activation_end_inset(place, message, at)
619
+ end
620
+
621
+ def compact_self_place(place, message, at)
622
+ depth = compact_activation_depth(place)
623
+ return at if depth.zero?
624
+
625
+ side = message.leftward? ? -1 : 1
626
+ at + ((depth - 1 + side) * (BarTracker::WIDTH / 2))
627
+ end
628
+
629
+ def compact_activation_place(place, message, at)
630
+ depth = compact_activation_depth(place)
631
+ return at if depth.zero?
632
+
633
+ other = place == message.to ? message.from : message.to
634
+ toward = activation_direction(other, at)
635
+ at + ((depth - 1) * (BarTracker::WIDTH / 2)) +
636
+ (toward * (BarTracker::WIDTH / 2))
637
+ end
638
+
639
+ def activation_direction(other, at)
640
+ return other.left? ? -1 : 1 if other.is_a?(Edge)
641
+
642
+ centre(other) < at ? -1 : 1
643
+ end
644
+
645
+ def compact_activation_depth(place)
646
+ opening = @following_activations.count do |activation|
647
+ activation.participant == place && activation.on?
648
+ end
649
+ @tracker.depth(place) + opening
650
+ end
651
+
652
+ def activation_end_inset(place, message, at)
653
+ other = place == message.to ? message.from : message.to
654
+ direction = centre(other) < at ? -1 : 1
655
+ direction * ACTIVATION_END_INSET
656
+ end
657
+
658
+ def activated_end?(place, message)
659
+ !@label_inset && !message.self_message? &&
660
+ @message_activations.include?(place)
661
+ end
662
+
663
+ def edge_x(edge, message)
664
+ return local_x(edge, message) if edge.local?
665
+
666
+ return @edge_right unless edge.left?
667
+
668
+ @label_inset ? 10.0 : LEFT_EDGE
669
+ end
670
+
671
+ def local_x(edge, message)
672
+ run = label_width(message) + Edge::RUN_PADDING
673
+ centre(message.participants.first) + (edge.left? ? -run : run)
674
+ end
675
+
676
+ # A bar starts or ends at the arrow it is written on, or else
677
+ # between the rows on either side of the line.
678
+ def activation(item)
679
+ @tracker.apply(item, bar_edge(item))
680
+ return if item.on? || !@next_non_activation.is_a?(Divider)
681
+
682
+ @y += @mark_y ? @deactivation_gap : @bare_deactivation_gap
683
+ end
684
+
685
+ def bar_edge(item)
686
+ return @y - 20 unless @mark_y
687
+
688
+ loop_closing = !item.on? && classic? && @self_drop.positive?
689
+ loop_closing ? @mark_y + CLOSING_LOOP_BAR_REACH : @mark_y
690
+ end
691
+
692
+ # The cross sits at the end of the arrow it follows, or else
693
+ # between the rows on either side of the line.
694
+ def destroy(item)
695
+ middle = @mark_y ? @mark_y + @self_drop : @y - 20
696
+ x = centre(item.participant)
697
+ @tracker.destroy(item.participant, middle) if @activation_edges
698
+ touch(x - CROSS_HALF, x + CROSS_HALF)
699
+ [-CROSS_HALF, CROSS_HALF].each do |slant|
700
+ @crosses << cross_stroke(x, middle, slant)
701
+ end
702
+ end
703
+
704
+ def cross_stroke(centre_x, middle, slant)
705
+ PlantUML::Scene::Segment.new(
706
+ x1: centre_x - CROSS_HALF, y1: middle - slant,
707
+ x2: centre_x + CROSS_HALF, y2: middle + slant
708
+ )
709
+ end
710
+
711
+ def arrow_for(message, from, to)
712
+ return straight(message, from, to) unless message.self_message?
713
+
714
+ self_arrow(message, from)
715
+ end
716
+
717
+ def loops_left?(message)
718
+ message.self_message? && message.leftward?
719
+ end
720
+
721
+ def centre(id)
722
+ @centers.fetch(@ids.index(id))
723
+ end
724
+
725
+ def reach(message)
726
+ return self_width + 6 + label_width(message) if @label_inset
727
+
728
+ [self_width, CLASSIC_LABEL_INSET + label_width(message)].max
729
+ end
730
+
731
+ def label_width(message)
732
+ NumberedLabel.new(@message_measure).extra(message) +
733
+ wrap.width(message.label.to_s)
734
+ end
735
+
736
+ def straight(message, from, to)
737
+ sign = to >= from ? 1 : -1
738
+ to -= sign * 2.0 if compact_filled_head?(message)
739
+ tail, start = ArrowMarks.for(message.style.tail, from, @y, sign)
740
+ head, stop = ArrowMarks.for(message.style.head, to, @y, -sign)
741
+ { path: "M #{start} #{@y} L #{stop} #{@y}", marks: tail + head,
742
+ label: straight_label(from, to) }
743
+ end
744
+
745
+ def compact_filled_head?(message)
746
+ @label_inset && message.style.head.glyph == :filled &&
747
+ !message.style.head.circle
748
+ end
749
+
750
+ def straight_label(from, to)
751
+ return [(from + to) / 2, @y - 6, "middle"] unless @label_inset
752
+
753
+ [[from, to].min + @label_inset, @y - 5.0659, "start"]
754
+ end
755
+
756
+ # The loop leaves and returns on the same side of the lifeline; a
757
+ # message written `<-` loops out on the left.
758
+ def self_arrow(message, centre)
759
+ side = loops_left?(message) ? -1 : 1
760
+ far = centre + (side * self_reach(message))
761
+ top = self_top
762
+ bottom = top + self_height
763
+ marks, start, stop = self_marks(message, centre, top, bottom, side)
764
+ { path: "M #{start} #{top} L #{far} #{top} L #{far} #{bottom} " \
765
+ "L #{stop} #{bottom}",
766
+ marks: marks, label: loop_label(far, side) }
767
+ end
768
+
769
+ def self_width
770
+ CLASSIC_SELF_WIDTH
771
+ end
772
+
773
+ def self_reach(message)
774
+ return self_width unless @label_inset && message.leftward?
775
+
776
+ spread = [compact_activation_depth(message.from) - 1, 0].max
777
+ self_width + (spread * (BarTracker::WIDTH / 2))
778
+ end
779
+
780
+ def self_marks(message, centre, top, bottom, side)
781
+ tail, start = ArrowMarks.for(message.style.tail, centre, top, side)
782
+ head, stop = ArrowMarks.for(message.style.head, centre, bottom,
783
+ side)
784
+ [tail + head, start, stop]
785
+ end
786
+
787
+ # The label rides above the loop, inset from the lifeline, on the
788
+ # side the loop leaves by.
789
+ def loop_label(far, side)
790
+ centre = far - (side * SELF_WIDTH)
791
+ rise = @label_inset ? 5.0659 : CLASSIC_LABEL_RISE
792
+ [centre + (side * (@label_inset || CLASSIC_LABEL_INSET)),
793
+ @y - rise, side.positive? ? "start" : "end"]
794
+ end
795
+
796
+ def self_height
797
+ LOOP_HEIGHT
798
+ end
799
+
800
+ def self_top
801
+ return @y if @teoz_fragment_cadence
802
+
803
+ return @y unless @label_inset && @fragmented
804
+
805
+ @y + ((SELF_HEIGHT - self_height) / 2)
806
+ end
807
+
808
+ def arrow_record(message, geometry)
809
+ Scene::Arrow.new(
810
+ id: "message-#{@arrows.size + 1}", path: geometry[:path],
811
+ dashed: message.dashed, colour: message.style.colour,
812
+ marks: geometry[:marks],
813
+ texts: label_texts(message, geometry[:label])
814
+ )
815
+ end
816
+
817
+ def label_texts(message, geometry)
818
+ return [] unless message.label && !message.label.empty?
819
+ return numbered_texts(message, geometry) if message.number
820
+
821
+ stacked_texts(message, *geometry)
822
+ end
823
+
824
+ def numbered_texts(message, geometry)
825
+ NumberedLabel.new(@message_measure,
826
+ size: compact_message_size,
827
+ family: @message_family)
828
+ .texts(message, *geometry)
829
+ end
830
+
831
+ def compact_message_size
832
+ @message_font_size if @message_family
833
+ end
834
+
835
+ def stacked_texts(message, at_x, at_y, anchor)
836
+ lines = label_lines(message)
837
+ lines.each_with_index.map do |line, index|
838
+ up = (lines.size - 1 - index) * line_step
839
+ text(line, at_x, at_y - up, "message_label", anchor)
840
+ end
841
+ end
842
+
843
+ def label_lines(message)
844
+ return [] if message.label.to_s.empty?
845
+
846
+ wrap.lines(message.label)
847
+ end
848
+
849
+ # PlantUML stacks wrapped lines 1.1776 of the text size apart.
850
+ def line_step
851
+ @message_font_size * LINE_LEADING
852
+ end
853
+
854
+ def text(content, at_x, at_y, role, anchor = "middle")
855
+ message = role == "message_label"
856
+ PlantUML::Scene::Text.new(
857
+ content: content, x: at_x, y: at_y, role: role, anchor: anchor,
858
+ size: (@message_font_size if message && @message_family),
859
+ family: (@message_family if message)
860
+ )
861
+ end
862
+
863
+ def touch(low, high)
864
+ @left = [@left, low].min
865
+ @right = [@right, high].max
866
+ return if @blocks.empty?
867
+
868
+ @blocks.last[:low] = [@blocks.last[:low], low].min
869
+ @blocks.last[:high] = [@blocks.last[:high], high].max
870
+ end
871
+
872
+ def note(note, previous)
873
+ on_block = note.attached? && previous.is_a?(Fragment)
874
+ x, width = on_block ? block_box(note) : note_box(note, previous)
875
+ height = NoteGeometry.height(note)
876
+ top = note_top(note, on_block, height)
877
+ @notes << note_record(note, [x, top, width, height])
878
+ touch(x, x + width)
879
+ pad = trailing_pad(note, previous, on_block)
880
+ clear_note(note, top + height, pad)
881
+ end
882
+
883
+ def clear_note(note, bottom, pad)
884
+ @y = [@y, bottom + note_tail(note)].max + pad
885
+ end
886
+
887
+ def trailing_pad(note, previous, on_block)
888
+ return 0.0 if on_block || !note.attached?
889
+
890
+ attached_note_pad(previous, note)
891
+ end
892
+
893
+ def note_tail(note)
894
+ note.parallel? || note.attached? ? 22 : STANDALONE_NOTE_TAIL
895
+ end
896
+
897
+ def note_top(note, on_block, height)
898
+ return @closed[:top] + 5 if on_block
899
+ if note.attached? && note.picture
900
+ return @last_y - (height / 2) + 10
901
+ end
902
+
903
+ return @last_y - height + ATTACHED_NOTE_RISE if note.attached?
904
+
905
+ @y - STANDALONE_NOTE_RISE
906
+ end
907
+
908
+ def block_box(note)
909
+ natural = NoteGeometry.natural_width(note, @note_font_size)
910
+ NoteGeometry.beside(@closed[:shape], natural)
911
+ end
912
+
913
+ def note_box(note, previous)
914
+ span = NoteGeometry.span(note, previous, @ids)
915
+ natural = NoteGeometry.natural_width(note, @note_font_size)
916
+ NoteGeometry.box(note, span.map { |i| @centers[i] }, natural)
917
+ end
918
+
919
+ def note_record(note, box)
920
+ x, top, width, height = box
921
+ Scene::Note.new(
922
+ path: NoteShape.outline(note.shape, x, top, width, height),
923
+ fold_path: NoteShape.fold(note.shape, x, top, width),
924
+ fill: note.fill&.colour, fill_opacity: note.fill&.opacity,
925
+ texts: note_texts(note, x + NoteGeometry::PAD, top),
926
+ picture: note_picture(note, x + NoteGeometry::PAD, top + 7)
927
+ )
928
+ end
929
+
930
+ def note_picture(note, left, top)
931
+ return unless note.picture
932
+
933
+ inset = NoteGeometry.picture_inset(note.picture)
934
+ Scene::Picture.new(x: left - inset, y: top - inset,
935
+ scale: note.picture.scale,
936
+ scene: note.picture.scene)
937
+ end
938
+
939
+ def note_texts(note, left, top)
940
+ return [] if note.picture
941
+
942
+ step = NoteGeometry.line_height(@note_font_size)
943
+ note.lines.each_with_index.map do |line, index|
944
+ baseline = top + NoteGeometry::FIRST_BASELINE + (index * step)
945
+ text(line, left, baseline, "note", "start")
946
+ end
947
+ end
948
+
949
+ # Between the rows on either side of the line, like a divider.
950
+ def page_break(_item)
951
+ @page_breaks << (@y - 20)
952
+ end
953
+
954
+ def ref(item)
955
+ shape = ref_shape(item)
956
+ @fragments << shape.scene
957
+ touch(shape.x, shape.x + shape.width)
958
+ @y += RefShape::HEIGHT + RefShape::TAIL
959
+ end
960
+
961
+ def ref_shape(item)
962
+ span = item.targets.map { |id| centre(id) }.minmax
963
+ RefShape.new(item, @y - @frame_bottom_offset, span, @measure)
964
+ end
965
+
966
+ def fragment(item)
967
+ case item.phase
968
+ when :open then open_block(item)
969
+ when :else then branch(item)
970
+ else close_block
971
+ end
972
+ end
973
+
974
+ def open_block(item)
975
+ @y += @fragment_contacts.fetch(@item_index, 0.0)
976
+ @blocks << { keyword: item.keyword, label: item.label.to_s,
977
+ top: @y - @frame_top_offset, low: Float::INFINITY,
978
+ high: -Float::INFINITY, branches: [], depth: 0,
979
+ resume: @resume, tab: tab_size, text_height: 0.0 }
980
+ @y += fragment_row(item) + tab_size.height_growth
981
+ end
982
+
983
+ def fragment_row(item)
984
+ return teoz_header_row if @teoz_fragment_cadence
985
+
986
+ return @group_fragment_row if group?(item) && @group_fragment_row
987
+
988
+ @fragment_row
989
+ end
990
+
991
+ def teoz_header_row
992
+ following = @next_non_activation
993
+ if following.is_a?(Fragment) && following.phase == :open
994
+ return TEOZ_NESTED_HEADER_ROW
995
+ end
996
+
997
+ if following.is_a?(Message) && !following.label.to_s.empty?
998
+ return TEOZ_MESSAGE_HEADER_ROW
999
+ end
1000
+
1001
+ TEOZ_EMPTY_HEADER_ROW
1002
+ end
1003
+
1004
+ def fragment_contacts(items)
1005
+ return {} unless @teoz_fragment_cadence
1006
+
1007
+ fragment_ends(items).transform_values do |index|
1008
+ message = following_parallel_self(items, index)
1009
+ message ? self_contact(message) : 0.0
1010
+ end
1011
+ end
1012
+
1013
+ def fragment_ends(items)
1014
+ stack = []
1015
+ items.each_with_index.with_object({}) do |(item, index), ends|
1016
+ next unless item.is_a?(Fragment)
1017
+
1018
+ stack << index if item.phase == :open
1019
+ ends[stack.pop] = index if item.phase == :close
1020
+ end
1021
+ end
1022
+
1023
+ def following_parallel_self(items, index)
1024
+ following = items.drop(index + 1).find do |item|
1025
+ !item.is_a?(Activation)
1026
+ end
1027
+ return unless following.is_a?(Message) && following.parallel?
1028
+
1029
+ following if following.self_message?
1030
+ end
1031
+
1032
+ def self_contact(message)
1033
+ text_height = label_lines(message).size * TEOZ_LABEL_HEIGHT
1034
+ text_height + (LOOP_HEIGHT / 2) + TEOZ_CONTACT_PADDING
1035
+ end
1036
+
1037
+ def group?(fragment)
1038
+ fragment.keyword == "group"
1039
+ end
1040
+
1041
+ def tab_size
1042
+ TabSize.new(@appearance.tab_size, @font_size)
1043
+ end
1044
+
1045
+ def branch(item)
1046
+ @blocks.last[:branches] << [@y - @branch_offset, item.label.to_s]
1047
+ @y += branch_row + classic_guard_row(item)
1048
+ end
1049
+
1050
+ def classic_guard_row(item)
1051
+ return 0.0 if @label_inset || item.label.to_s.strip.empty?
1052
+
1053
+ CLASSIC_GUARD_ROW
1054
+ end
1055
+
1056
+ def branch_row
1057
+ return @branch_gap unless @teoz_fragment_cadence
1058
+
1059
+ following = @next_non_activation
1060
+ if following.is_a?(Message) && following.label.to_s.empty?
1061
+ return @branch_gap - TEOZ_LABEL_HEIGHT
1062
+ end
1063
+
1064
+ @branch_gap
1065
+ end
1066
+
1067
+ def close_block
1068
+ block = @blocks.pop
1069
+ bottom = @y - fragment_bottom_offset(block)
1070
+ @y = [@y + close_gap(block), block[:resume]].compact.max
1071
+ @row_line = block[:top]
1072
+ emit_fragment(block, bottom)
1073
+ end
1074
+
1075
+ def fragment_bottom_offset(block)
1076
+ return @frame_bottom_offset unless @teoz_fragment_cadence
1077
+
1078
+ block[:text_height] + TEOZ_FRAME_BOTTOM_PAD
1079
+ end
1080
+
1081
+ def close_gap(block)
1082
+ if @teoz_fragment_cadence
1083
+ return teoz_root_close_gap(block) if @blocks.empty?
1084
+
1085
+ return TEOZ_NESTED_CLOSE_GAP
1086
+ end
1087
+ return @group_close_gap if group_close_gap?(block)
1088
+
1089
+ @fragment_close_gap
1090
+ end
1091
+
1092
+ def teoz_root_close_gap(block)
1093
+ following = @next_non_activation
1094
+ return TEOZ_ROOT_CLOSE_GAP unless following.is_a?(Message)
1095
+ return TEOZ_ROOT_CLOSE_GAP if following.parallel?
1096
+
1097
+ next_text = following.label.to_s.empty? ? 0.0 : TEOZ_LABEL_HEIGHT
1098
+ TEOZ_NESTED_CLOSE_GAP + next_text - block[:text_height]
1099
+ end
1100
+
1101
+ def group_close_gap?(block)
1102
+ block[:keyword] == "group" && @group_close_gap
1103
+ end
1104
+
1105
+ def emit_fragment(block, bottom)
1106
+ record = FragmentShape.new(block, bottom, @centers, @measure,
1107
+ @appearance)
1108
+ @fragments << record.scene
1109
+ @closed = { shape: record, top: block[:top] }
1110
+ touch(record.x, record.x + record.width)
1111
+ deepen_parent(block)
1112
+ end
1113
+
1114
+ def deepen_parent(block)
1115
+ parent = @blocks.last
1116
+ return unless parent
1117
+
1118
+ parent[:depth] = [parent[:depth], block[:depth] + 1].max
1119
+ parent[:text_height] = block[:text_height]
1120
+ end
1121
+
1122
+ def spacing(item)
1123
+ @y += item.room
1124
+ end
1125
+
1126
+ # The lifelines run on through the gap, dotted over it in a classic
1127
+ # diagram.
1128
+ def delay(item)
1129
+ label = item.label.to_s
1130
+ return plain_delay(label) if @label_inset || @teoz_fragment_cadence
1131
+
1132
+ gap = label.empty? ? CLASSIC_DELAY_ROW : CLASSIC_DELAY_LABEL_ROW
1133
+ top = @y - CLASSIC_LABEL_ROW
1134
+ @delays << delay_record(label, top, top + gap,
1135
+ CLASSIC_DELAY_TEXT_DROP)
1136
+ @y = top + gap + delay_clearance
1137
+ end
1138
+
1139
+ # What the next message's row needs below the gap.
1140
+ def delay_clearance
1141
+ following_unlabelled_message? ? 6.0 : CLASSIC_LABEL_ROW
1142
+ end
1143
+
1144
+ def plain_delay(label)
1145
+ @delays << delay_record(label, @y, @y, 5) unless label.empty?
1146
+ @y += @divider_row
1147
+ end
1148
+
1149
+ def delay_record(label, top, bottom, drop)
1150
+ texts = label.empty? ? [] : [delay_text(label, top + drop)]
1151
+ Scene::Delay.new(y1: top, y2: bottom, texts: texts)
1152
+ end
1153
+
1154
+ def delay_text(label, at_y)
1155
+ middle = (@bounds[0] + @bounds[1]) / 2
1156
+ half = @measure.call(label) / 2
1157
+ touch(middle - half, middle + half)
1158
+ text(label, middle, at_y, "delay")
1159
+ end
1160
+
1161
+ def divider(item)
1162
+ left, right = @bounds
1163
+ label = item.label.to_s
1164
+ @dividers << divider_record(label, left, right)
1165
+ @y += @divider_row
1166
+ end
1167
+
1168
+ def divider_record(label, left, right)
1169
+ width = label.empty? ? 0.0 : @measure.call(label) + 20
1170
+ middle = (left + right) / 2
1171
+ Scene::Divider.new(
1172
+ lines: [-2, 2].map { |dy| segment(left, right, @y + dy) },
1173
+ x: middle - (width / 2), y: @y - 10, width: width, height: 20.0,
1174
+ texts: divider_texts(label, middle)
1175
+ )
1176
+ end
1177
+
1178
+ def divider_texts(label, middle)
1179
+ label.empty? ? [] : [text(label, middle, @y + 5, "divider")]
1180
+ end
1181
+
1182
+ def segment(left, right, at_y)
1183
+ PlantUML::Scene::Segment.new(x1: left, y1: at_y,
1184
+ x2: right, y2: at_y)
1185
+ end
1186
+ end
1187
+ end
1188
+ end
1189
+ end
1190
+ end