Draw a line-end marker (arrow / dot / diamond) as plain SVG elements.
(parent, p_from, p_to, style: str, lw: float, color: str)
| 606 | |
| 607 | |
| 608 | def _svg_line_end(parent, p_from, p_to, style: str, lw: float, color: str) -> None: |
| 609 | """Draw a line-end marker (arrow / dot / diamond) as plain SVG elements.""" |
| 610 | import math |
| 611 | if style == "none": |
| 612 | return |
| 613 | dx, dy = p_to[0] - p_from[0], p_to[1] - p_from[1] |
| 614 | length = math.hypot(dx, dy) |
| 615 | if length < 1e-6: |
| 616 | return |
| 617 | size = max(16.0, lw * 6) |
| 618 | ux, uy = dx / length, dy / length |
| 619 | base = {"stroke": color, "stroke-width": f"{lw:.2f}", |
| 620 | "stroke-linecap": "round"} |
| 621 | |
| 622 | if style == "arrow": |
| 623 | px_, py_ = -uy, ux |
| 624 | for side in (1, -1): |
| 625 | ax = p_to[0] - size * ux + side * size * 0.4 * px_ |
| 626 | ay = p_to[1] - size * uy + side * size * 0.4 * py_ |
| 627 | al = ET.SubElement(parent, f"{{{_SVG_NS}}}line") |
| 628 | al.set("x1", f"{p_to[0]:.2f}"); al.set("y1", f"{p_to[1]:.2f}") |
| 629 | al.set("x2", f"{ax:.2f}"); al.set("y2", f"{ay:.2f}") |
| 630 | al.set("fill", "none") |
| 631 | for k, v in base.items(): al.set(k, v) |
| 632 | |
| 633 | elif style == "dot": |
| 634 | r = size * 0.35 |
| 635 | el = ET.SubElement(parent, f"{{{_SVG_NS}}}circle") |
| 636 | el.set("cx", f"{p_to[0]:.2f}"); el.set("cy", f"{p_to[1]:.2f}") |
| 637 | el.set("r", f"{r:.2f}") |
| 638 | el.set("fill", color); el.set("stroke", "none") |
| 639 | |
| 640 | elif style == "diamond": |
| 641 | h, w = size * 0.9, size * 0.4 |
| 642 | px_, py_ = -uy, ux |
| 643 | tip = p_to |
| 644 | back = (p_to[0] - h*ux, p_to[1] - h*uy) |
| 645 | left = (p_to[0] - h/2*ux + w*px_, p_to[1] - h/2*uy + w*py_) |
| 646 | rght = (p_to[0] - h/2*ux - w*px_, p_to[1] - h/2*uy - w*py_) |
| 647 | pts_str = " ".join(f"{x:.2f},{y:.2f}" |
| 648 | for x, y in [tip, left, back, rght]) |
| 649 | el = ET.SubElement(parent, f"{{{_SVG_NS}}}polygon") |
| 650 | el.set("points", pts_str) |
| 651 | el.set("fill", color); el.set("stroke", "none") |
| 652 | |
| 653 | |
| 654 | def _hyperlink_block(parent, item, color, fs, family, underline: bool): |