Line joints and caps¶
Every Line2D joint, cap and gradient side by side.
▶ Run in browserTags: 2d
A Line2D is more than a run of segments. Where it bends it leaves a notch,
and joint_mode decides what fills it; where it ends it stops flat, and
begin_cap_mode / end_cap_mode decide whether it rounds off; and gradient
colours it along its own length instead of flat.
How it works¶
Four rows, each the same zigzag drawn with a different setting so the difference is visible rather than described:
joint_modeis"sharp"(a miter),"bevel"(a flat corner) or"round"(an arc). A miter that would shoot a spike off a near-reversal falls back to a bevel on its own.begin_cap_modeandend_cap_modeare"none"or"round", and each end is set independently.gradientis a list of(t, colour)stops.truns from 0 at the first point to 1 at the last, measured along the line, and the gradient replacescolourrather than tinting it.A
widthof 1.0 or less is a hairline: it rides the one-pixel line pipeline whatever the camera is doing, so it has no width for a joint or a cap to fill and neither is drawn.
The alpha on the top row is deliberately low: a joint fills the outside of a bend only, so a translucent line does not darken along its own corners.
Source¶
1"""Line joints and caps: Every Line2D joint, cap and gradient side by side.
2
3A `Line2D` is more than a run of segments. Where it bends it leaves a notch,
4and `joint_mode` decides what fills it; where it ends it stops flat, and
5`begin_cap_mode` / `end_cap_mode` decide whether it rounds off; and `gradient`
6colours it along its own length instead of flat.
7
8## How it works
9
10Four rows, each the same zigzag drawn with a different setting so the
11difference is visible rather than described:
12
131. `joint_mode` is `"sharp"` (a miter), `"bevel"` (a flat corner) or `"round"`
14 (an arc). A miter that would shoot a spike off a near-reversal falls back to
15 a bevel on its own.
162. `begin_cap_mode` and `end_cap_mode` are `"none"` or `"round"`, and each end
17 is set independently.
183. `gradient` is a list of `(t, colour)` stops. `t` runs from 0 at the first
19 point to 1 at the last, measured along the line, and the gradient replaces
20 `colour` rather than tinting it.
214. A `width` of 1.0 or less is a hairline: it rides the one-pixel line pipeline
22 whatever the camera is doing, so it has no width for a joint or a cap to
23 fill and neither is drawn.
24
25The alpha on the top row is deliberately low: a joint fills the outside of a
26bend only, so a translucent line does not darken along its own corners.
27"""
28
29from simvx.core import Line2D, Node2D
30from simvx.graphics import App
31
32WIDTH, HEIGHT = 900, 620
33
34#: A right angle, an acute bend and a near-reversal, so every joint mode has
35#: something to show.
36ZIGZAG = [(0, 60), (60, 60), (60, 0), (140, 70), (200, 10), (150, 14)]
37
38ROWS = (
39 (60.0, "joint_mode: sharp, bevel, round"),
40 (210.0, "begin_cap_mode / end_cap_mode: none, both, end only"),
41 (360.0, "gradient: a ramp, a hard stop at the midpoint, four stops"),
42 (510.0, "width 1.0 or less is a hairline: no joint, no cap"),
43)
44
45
46def _at(row: int, column: int) -> tuple[float, float]:
47 return 60.0 + column * 280.0, ROWS[row][0]
48
49
50class LineGallery(Node2D):
51 def on_ready(self):
52 for column, mode in enumerate(("sharp", "bevel", "round")):
53 self.add_child(
54 Line2D(
55 name=f"Joint{mode.title()}",
56 position=_at(0, column),
57 points=ZIGZAG,
58 width=14.0,
59 colour=(0.3, 0.85, 1.0, 0.45),
60 joint_mode=mode,
61 )
62 )
63
64 caps = (("none", "none"), ("round", "round"), ("none", "round"))
65 for column, (begin, end) in enumerate(caps):
66 self.add_child(
67 Line2D(
68 name=f"Cap{begin.title()}{end.title()}",
69 position=_at(1, column),
70 points=ZIGZAG,
71 width=14.0,
72 colour=(1.0, 0.72, 0.25, 1.0),
73 joint_mode="round",
74 begin_cap_mode=begin,
75 end_cap_mode=end,
76 )
77 )
78
79 gradients = (
80 [(0.0, (1.0, 0.2, 0.3, 1.0)), (1.0, (0.25, 0.4, 1.0, 1.0))],
81 [
82 (0.0, (1.0, 0.85, 0.2, 1.0)),
83 (0.5, (1.0, 0.85, 0.2, 1.0)),
84 (0.5, (0.2, 0.9, 0.5, 1.0)),
85 (1.0, (0.2, 0.9, 0.5, 1.0)),
86 ],
87 [
88 (0.0, (1.0, 0.3, 0.3, 1.0)),
89 (0.33, (1.0, 0.9, 0.3, 1.0)),
90 (0.66, (0.3, 1.0, 0.6, 1.0)),
91 (1.0, (0.4, 0.5, 1.0, 1.0)),
92 ],
93 )
94 for column, gradient in enumerate(gradients):
95 self.add_child(
96 Line2D(
97 name=f"Gradient{column}",
98 position=_at(2, column),
99 points=ZIGZAG,
100 width=14.0,
101 joint_mode="round",
102 gradient=gradient,
103 )
104 )
105
106 for column, width in enumerate((1.0, 3.0, 14.0)):
107 self.add_child(
108 Line2D(
109 name=f"Weight{column}",
110 position=_at(3, column),
111 points=ZIGZAG,
112 width=width,
113 colour=(0.9, 0.9, 0.95, 1.0),
114 joint_mode="round",
115 begin_cap_mode="round",
116 end_cap_mode="round",
117 )
118 )
119
120 def on_draw(self, renderer):
121 for y, title in ROWS:
122 renderer.draw_text(title, (40, y - 34), scale=1, colour=(0.75, 0.78, 0.85, 1.0))
123
124
125if __name__ == "__main__":
126 App(title="Line joints and caps", width=WIDTH, height=HEIGHT).run(LineGallery())