Class: Ea::Svg::EaEmitter::Markers
- Inherits:
-
Object
- Object
- Ea::Svg::EaEmitter::Markers
- Defined in:
- lib/ea/svg/ea_emitter/markers.rb
Overview
Emits marker <polygon> and <path> elements per connector,
matching EA's encoding rules. Returns an Array of Layer
structs bucketed by marker style.
Layer style keys:
- :diamond_filled → 4-pt <polygon>, black fill
- :triangle_open → 3-pt <polygon>, white fill
- :arrow → 3-pt <path>, no fill (shares style with
connector lines — same stroke, no fill)
Default mode (grouped: true) consolidates all markers of
the same style into ONE <g>. Pass grouped: false for
per-connector grouping.
Defined Under Namespace
Classes: Entry
Constant Summary collapse
- DIAMOND_HALF_W =
5- DIAMOND_HALF_H =
10- TRI_HALF_BASE =
6- TRI_HEIGHT =
11- ARROW_HALF_BASE =
6- ARROW_HEIGHT =
11
Instance Attribute Summary collapse
-
#canvas ⇒ Object
readonly
Returns the value of attribute canvas.
-
#diagram ⇒ Object
readonly
Returns the value of attribute diagram.
-
#grouped ⇒ Object
readonly
Returns the value of attribute grouped.
-
#model_index ⇒ Object
readonly
Returns the value of attribute model_index.
-
#stroke_width ⇒ Object
readonly
Returns the value of attribute stroke_width.
Instance Method Summary collapse
- #arrow_path(tip, base) ⇒ Object
-
#count_for(connector) ⇒ Object
Backwards-compatible API: count of marker bodies emitted.
- #diamond_polygon(tip, base) ⇒ Object
-
#initialize(diagram, model_index:, canvas: nil, grouped: true, stroke_width: 2) ⇒ Markers
constructor
A new instance of Markers.
- #layers ⇒ Object
- #package_anchor_path(tip, base) ⇒ Object
-
#plus_path(anchor) ⇒ Object
Plus marker: two crossed line segments at the anchor.
- #render ⇒ Object
- #triangle_polygon(tip, base) ⇒ Object
Constructor Details
#initialize(diagram, model_index:, canvas: nil, grouped: true, stroke_width: 2) ⇒ Markers
Returns a new instance of Markers.
29 30 31 32 33 34 35 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 29 def initialize(diagram, model_index:, canvas: nil, grouped: true, stroke_width: 2) @diagram = diagram @model_index = model_index @canvas = canvas @grouped = grouped @stroke_width = stroke_width end |
Instance Attribute Details
#canvas ⇒ Object (readonly)
Returns the value of attribute canvas.
27 28 29 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 27 def canvas @canvas end |
#diagram ⇒ Object (readonly)
Returns the value of attribute diagram.
27 28 29 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 27 def diagram @diagram end |
#grouped ⇒ Object (readonly)
Returns the value of attribute grouped.
27 28 29 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 27 def grouped @grouped end |
#model_index ⇒ Object (readonly)
Returns the value of attribute model_index.
27 28 29 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 27 def model_index @model_index end |
#stroke_width ⇒ Object (readonly)
Returns the value of attribute stroke_width.
27 28 29 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 27 def stroke_width @stroke_width end |
Instance Method Details
#arrow_path(tip, base) ⇒ Object
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 358 def arrow_path(tip, base) tx, ty = translate_point(tip) bx, by = translate_point(base) ux, uy = unit_vector(tx, ty, bx, by) return nil unless ux back_x = tx - ux * ARROW_HEIGHT back_y = ty - uy * ARROW_HEIGHT w1_x = back_x + (-uy) * ARROW_HALF_BASE w1_y = back_y + ux * ARROW_HALF_BASE w2_x = back_x - (-uy) * ARROW_HALF_BASE w2_y = back_y - ux * ARROW_HALF_BASE d = "M #{Canvas.coord(w1_x)} #{Canvas.coord(w1_y)} L #{Canvas.coord(tx)} #{Canvas.coord(ty)} L #{Canvas.coord(w2_x)} #{Canvas.coord(w2_y)}" %(<path d="#{d}" shape-rendering="auto"/>) end |
#count_for(connector) ⇒ Object
Backwards-compatible API: count of marker bodies emitted.
63 64 65 66 67 68 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 63 def count_for(connector) return 0 if connector.hidden return 0 unless connector.waypoints&.any?(&:position) entries_for(connector).size end |
#diamond_polygon(tip, base) ⇒ Object
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 317 def diamond_polygon(tip, base) tx, ty = translate_point(tip) bx, by = translate_point(base) ux, uy = unit_vector(tx, ty, bx, by) return nil unless ux right_x = tx - ux * DIAMOND_HALF_H + (-uy) * DIAMOND_HALF_W right_y = ty - uy * DIAMOND_HALF_H + ux * DIAMOND_HALF_W back_x = tx - ux * (DIAMOND_HALF_H * 2) back_y = ty - uy * (DIAMOND_HALF_H * 2) left_x = tx - ux * DIAMOND_HALF_H - (-uy) * DIAMOND_HALF_W left_y = ty - uy * DIAMOND_HALF_H - ux * DIAMOND_HALF_W pts = [ Canvas.coord(tx), Canvas.coord(ty), Canvas.coord(right_x), Canvas.coord(right_y), Canvas.coord(back_x), Canvas.coord(back_y), Canvas.coord(left_x), Canvas.coord(left_y) ].join(" ") %(<polygon points="#{pts}" shape-rendering="auto" style="fill-rule:evenodd;"/>) end |
#layers ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 37 def layers entries = visible_connectors.flat_map { |c| entries_for(c) } return [] if entries.empty? if grouped entries.group_by(&:style_key).map do |key, grouped_entries| # EA emits ONE marker per connector regardless of # anchor overlap. Verified against plateau Bridge # diagram: 9 generalizations fanning from the same # parent anchor produce 9 distinct triangle polygons # at the same coordinates. The earlier "dedup by # anchor" hypothesis is disproven by reference SVGs. Layer.new(style_key: key, style: style_for(key), body: grouped_entries.map(&:body).join("\n ")) end else entries.map { |e| Layer.new(style_key: e.style_key, style: style_for(e.style_key), body: e.body) } end end |
#package_anchor_path(tip, base) ⇒ Object
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 382 def package_anchor_path(tip, base) tx, ty = translate_point(tip) bx, by = translate_point(base) ux, uy = unit_vector(tx, ty, bx, by) return nil unless ux back_x = tx - ux * PACKAGE_ANCHOR_H_BOTTOM back_y = ty - uy * PACKAGE_ANCHOR_H_BOTTOM tl_x = tx - ux * PACKAGE_ANCHOR_H_TOP + (-uy) * PACKAGE_ANCHOR_W tl_y = ty - uy * PACKAGE_ANCHOR_H_TOP + ux * PACKAGE_ANCHOR_W tr_x = tx - ux * PACKAGE_ANCHOR_H_TOP - (-uy) * PACKAGE_ANCHOR_W tr_y = ty - uy * PACKAGE_ANCHOR_H_TOP - ux * PACKAGE_ANCHOR_W bl_x = back_x + (-uy) * (PACKAGE_ANCHOR_W + 1) bl_y = back_y + ux * (PACKAGE_ANCHOR_W + 1) br_x = back_x - (-uy) * (PACKAGE_ANCHOR_W + 1) br_y = back_y - ux * (PACKAGE_ANCHOR_W + 1) d = "M #{Canvas.coord(tl_x)} #{Canvas.coord(tl_y)} " \ "L #{Canvas.coord(tr_x)} #{Canvas.coord(tr_y)} " \ "L #{Canvas.coord(br_x)} #{Canvas.coord(br_y)} " \ "L #{Canvas.coord(bl_x)} #{Canvas.coord(bl_y)} " \ "L #{Canvas.coord(tl_x)} #{Canvas.coord(tl_y)} Z" %(<path d="#{d}" shape-rendering="auto"/>) end |
#plus_path(anchor) ⇒ Object
Plus marker: two crossed line segments at the anchor.
EA encodes as one
M cx cy-8 L cx cy+8 (vertical arm)
M cx+8 cy L cx-8 cy (horizontal arm)
270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 270 def plus_path(anchor) cx, cy = translate_point(anchor) cx_str = Canvas.coord(cx) cy_str = Canvas.coord(cy) up = Canvas.coord(cy - Marker::Plus::ARM_LENGTH) down = Canvas.coord(cy + Marker::Plus::ARM_LENGTH) left = Canvas.coord(cx - Marker::Plus::ARM_LENGTH) right = Canvas.coord(cx + Marker::Plus::ARM_LENGTH) d = "M #{cx_str} #{up} L #{cx_str} #{down} M #{right} #{cy_str} L #{left} #{cy_str}" %(<path d="#{d}" shape-rendering="auto"/>) end |
#render ⇒ Object
58 59 60 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 58 def render layers.map(&:to_svg).join("\n") end |
#triangle_polygon(tip, base) ⇒ Object
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/ea/svg/ea_emitter/markers.rb', line 338 def triangle_polygon(tip, base) tx, ty = translate_point(tip) bx, by = translate_point(base) ux, uy = unit_vector(tx, ty, bx, by) return nil unless ux back_x = tx - ux * TRI_HEIGHT back_y = ty - uy * TRI_HEIGHT w1_x = back_x + (-uy) * TRI_HALF_BASE w1_y = back_y + ux * TRI_HALF_BASE w2_x = back_x - (-uy) * TRI_HALF_BASE w2_y = back_y - ux * TRI_HALF_BASE pts = [ Canvas.coord(tx), Canvas.coord(ty), Canvas.coord(w1_x), Canvas.coord(w1_y), Canvas.coord(w2_x), Canvas.coord(w2_y) ].join(" ") %(<polygon points="#{pts}" shape-rendering="auto" style="fill-rule:evenodd;"/>) end |