|
2 | 2 | <svg |
3 | 3 | xmlns="http://www.w3.org/2000/svg" |
4 | 4 | class="release-cycle-chart" |
5 | | - viewBox="0 0 {{ diagram_width * SCALE }} {{ diagram_height * SCALE }}" |
| 5 | + viewBox="0 0 {{ diagram_width }} {{ diagram_height }}" |
6 | 6 | > |
7 | 7 | <defs> |
8 | 8 | <linearGradient id="release-cycle-mask-gradient-{{ id_key }}"> |
|
12 | 12 | </defs> |
13 | 13 |
|
14 | 14 | {% for version in versions %} |
15 | | - {% set y = version.y * LINE_HEIGHT %} |
| 15 | + {% set y = version.y * line_height %} |
16 | 16 |
|
17 | 17 | {% if version.y % 2 %} |
18 | 18 | <!-- Row shading --> |
19 | 19 | <rect |
20 | 20 | class="release-cycle-row-shade" |
21 | 21 | x="0em" |
22 | | - y="{{ (y - 1.125) * SCALE }}" |
23 | | - width="{{ diagram_width * SCALE }}" |
24 | | - height="{{ LINE_HEIGHT * SCALE }}" |
| 22 | + y="{{ y - 1.125 * SCALE }}" |
| 23 | + width="{{ diagram_width }}" |
| 24 | + height="{{ line_height }}" |
25 | 25 | /> |
26 | 26 | {% endif %} |
27 | 27 | {% endfor %} |
28 | 28 |
|
29 | 29 | {% for year in years %} |
30 | 30 | <text |
31 | 31 | class="release-cycle-year-text" |
32 | | - x="{{ (year_to_x(year) + year_to_x(year + 1)) / 2 * SCALE }}" |
33 | | - y="{{ (diagram_height - LINE_HEIGHT) * SCALE }}" |
| 32 | + x="{{ (year_to_x(year) + year_to_x(year + 1)) / 2 }}" |
| 33 | + y="{{ diagram_height - line_height }}" |
34 | 34 | font-size="{{ SCALE * 0.75 }}" |
35 | 35 | text-anchor="middle" |
36 | 36 | > |
|
39 | 39 | {% if not loop.last %} |
40 | 40 | <line |
41 | 41 | class="release-cycle-year-line" |
42 | | - x1="{{ year_to_x(year + 1) * SCALE }}" |
43 | | - x2="{{ year_to_x(year + 1) * SCALE }}" |
| 42 | + x1="{{ year_to_x(year + 1) }}" |
| 43 | + x2="{{ year_to_x(year + 1) }}" |
44 | 44 | y1="0" |
45 | | - y2="{{ (diagram_height - LINE_HEIGHT) * SCALE }}" |
| 45 | + y2="{{ diagram_height - line_height }}" |
46 | 46 | font-size="{{ SCALE }}" |
47 | 47 | /> |
48 | 48 | {% endif %} |
|
53 | 53 | <rect |
54 | 54 | x="0" |
55 | 55 | y="0" |
56 | | - width="{{ LEGEND_WIDTH * SCALE }}" |
57 | | - height="{{ diagram_height * SCALE }}" |
| 56 | + width="{{ legend_width }}" |
| 57 | + height="{{ diagram_height }}" |
58 | 58 | fill="black" |
59 | 59 | /> |
60 | 60 | <rect |
61 | | - x="{{ (LEGEND_WIDTH - RIGHT_MARGIN) * SCALE }}" |
| 61 | + x="{{ legend_width - right_margin }}" |
62 | 62 | y="0" |
63 | | - width="{{ RIGHT_MARGIN * SCALE }}" |
64 | | - height="{{ diagram_height * SCALE }}" |
| 63 | + width="{{ right_margin }}" |
| 64 | + height="{{ diagram_height }}" |
65 | 65 | fill="url(#release-cycle-mask-gradient-{{ id_key }})" |
66 | 66 | /> |
67 | 67 | <rect |
68 | | - x="{{ (LEGEND_WIDTH ) * SCALE }}" |
| 68 | + x="{{ legend_width }}" |
69 | 69 | y="0" |
70 | | - width="{{ diagram_width * SCALE }}" |
71 | | - height="{{ diagram_height * SCALE }}" |
| 70 | + width="{{ diagram_width }}" |
| 71 | + height="{{ diagram_height }}" |
72 | 72 | fill="white" |
73 | 73 | /> |
74 | 74 | </mask> |
75 | 75 |
|
76 | 76 | {% for version in versions %} |
77 | | - {% set y = version.y * LINE_HEIGHT %} |
| 77 | + {% set top_y = version.y * line_height - 1 * SCALE %} |
| 78 | + {% set small_text_y = version.y * line_height - 0.1 * SCALE %} |
| 79 | + |
| 80 | + <!-- Colourful blob with a label. --> |
78 | 81 |
|
79 | | - <!-- Colourful blob with a label --> |
80 | 82 | {% set start_x = date_to_x(version.first_release_date) %} |
81 | 83 | {% set end_x = date_to_x(version.end_of_life_date) %} |
82 | 84 |
|
83 | | - <!-- bugfix status needs a security tail. |
| 85 | + <!-- bugfix/security blobs need to be split between the two phases. |
84 | 86 | Draw the rectangle with two path elements instead. |
85 | | - Thanks Claude.ai for the conversion. |
| 87 | + Thanks Claude.ai for the initial conversion. |
86 | 88 | --> |
87 | | - {% set half_x = [end_x, date_to_x(version.start_security_date)]|min %} |
| 89 | + {% set middle_x = ([end_x, date_to_x(version.start_security_date)]|min) %} |
88 | 90 | {% set height = 1.25 * SCALE %} |
89 | | - {% set left_width = (half_x - start_x) * SCALE %} |
90 | | - {% set right_width = (end_x - half_x) * SCALE %} |
91 | | - {% set left_x = start_x * SCALE %} |
92 | | - {% set middle_x = half_x * SCALE %} |
93 | | - {% set right_x = half_x * SCALE %} |
94 | | - {% set rect_y = (y - 1) * SCALE %} |
95 | | - {% set radius_value = 0.25 * SCALE %} |
| 91 | + {% set left_width = (middle_x - start_x) %} |
| 92 | + {% set right_width = (end_x - middle_x) %} |
| 93 | + {% set radius = 0.25 * SCALE %} |
96 | 94 |
|
97 | 95 | {% if version.status != "end-of-life" %} |
98 | | - <!-- Split the blob --> |
| 96 | + <!-- Split the blob using path operations |
| 97 | + (Move-to, Vertical/Horizontal, Arc, Z=close shape; |
| 98 | + lowercase means relative to the last point.) |
| 99 | + We start drawing from the top of the straight boundary |
| 100 | + between the half-blobs. |
| 101 | + --> |
99 | 102 | <path |
100 | 103 | class="release-cycle-blob release-cycle-status-bugfix" |
101 | 104 | d=" |
102 | | - M{{ left_x + radius_value }},{{ rect_y }} |
103 | | - q{{ -radius_value }},0 {{ -radius_value }},{{ radius_value }} |
104 | | - v{{ height - 2*radius_value }} |
105 | | - q0,{{ radius_value }} {{ radius_value }},{{ radius_value }} |
106 | | - H{{ middle_x }} |
107 | | - V{{ rect_y }} |
108 | | - Z |
| 105 | + M{{ middle_x }},{{ top_y }} {#- start -#} |
| 106 | + v{{ height }} {#- down -#} |
| 107 | + H{{ start_x + radius }} {#- left -#} |
| 108 | + a{{ radius }},{{ radius }} 90 0 1 {#- rounded corner -#} |
| 109 | + {{ -radius }} {{ -radius }} |
| 110 | + v{{ -height + 2*radius }} {#- up -#} |
| 111 | + a{{ radius }},{{ radius }} 90 0 1 {#- rounded corner -#} |
| 112 | + {{ radius }} {{ -radius }} |
| 113 | + Z {#- right -#} |
109 | 114 | " |
110 | 115 | /> |
111 | 116 | <path |
112 | 117 | class="release-cycle-blob release-cycle-status-security" |
113 | 118 | d=" |
114 | | - M{{ right_x }},{{ rect_y }} |
115 | | - h{{ right_width - radius_value }} |
116 | | - q{{ radius_value }},0 {{ radius_value }},{{ radius_value }} |
117 | | - v{{ height - 2*radius_value }} |
118 | | - q0,{{ radius_value }} {{ -radius_value }},{{ radius_value }} |
119 | | - H{{ middle_x }} |
120 | | - V{{ rect_y }} |
121 | | - Z |
| 119 | + M{{ middle_x }},{{ top_y }} {#- start -#} |
| 120 | + v{{ height }} {#- down -#} |
| 121 | + H{{ end_x - radius }} {#- right -#} |
| 122 | + a{{ radius }},{{ radius }} 90 0 0 {#- rounded corner -#} |
| 123 | + {{ radius }} {{ -radius }} |
| 124 | + v{{ -height + 2*radius }} {#- up -#} |
| 125 | + a{{ radius }},{{ radius }} 90 0 0 {#- rounded corner -#} |
| 126 | + {{ -radius }} {{ -radius }} |
| 127 | + Z {#- left -#} |
122 | 128 | " |
123 | 129 | /> |
124 | 130 | {% endif %} |
125 | 131 | <rect |
126 | 132 | class="release-cycle-shade release-cycle-status-{{ version.status }}" |
127 | | - x="{{ start_x * SCALE }}" |
128 | | - y="{{ (y - 1) * SCALE }}" |
129 | | - width="{{ (end_x - start_x) * SCALE }}" |
| 133 | + x="{{ start_x }}" |
| 134 | + y="{{ top_y }}" |
| 135 | + width="{{ (end_x - start_x) }}" |
130 | 136 | height="{{ height }}" |
131 | 137 | rx="0.25em" |
132 | 138 | ry="0.25em" |
133 | 139 | mask="url(#release-cycle-mask-{{ id_key }})" |
134 | 140 | /> |
135 | 141 | <rect |
136 | 142 | class="release-cycle-border release-cycle-status-{{ version.status }}" |
137 | | - x="{{ start_x * SCALE }}" |
138 | | - y="{{ (y - 1) * SCALE }}" |
139 | | - width="{{ (end_x - start_x) * SCALE }}" |
| 143 | + x="{{ start_x }}" |
| 144 | + y="{{ top_y }}" |
| 145 | + width="{{ (end_x - start_x) }}" |
140 | 146 | height="{{ height }}" |
141 | 147 | rx="0.25em" |
142 | 148 | ry="0.25em" |
143 | 149 | mask="url(#release-cycle-mask-{{ id_key }})" |
144 | 150 | /> |
145 | | - {% if version.status == "bugfix" %} |
146 | | - <text |
147 | | - class="release-cycle-blob-label release-cycle-status-bugfix" |
148 | | - x="{{ (start_x + half_x) / 2 * SCALE }}" |
149 | | - y="{{ (y - 0.1) * SCALE }}" |
150 | | - font-size="{{ SCALE * 0.75 }}" |
| 151 | + <text |
| 152 | + class="release-cycle-blob-label release-cycle-status-{{ version.status }}" |
| 153 | + font-size="{{ SCALE * 0.75 }}" |
| 154 | + y="{{ small_text_y }}" |
| 155 | + {% if version.status == "bugfix" %} |
| 156 | + x="{{ (start_x + middle_x) / 2 }}" |
151 | 157 | text-anchor="middle" |
152 | | - > |
153 | | - bugfix |
154 | | - </text> |
155 | | - {% elif version.status == "security" %} |
156 | | - <text |
157 | | - class="release-cycle-blob-label release-cycle-status-security" |
158 | | - x="{{ (half_x + end_x) / 2 * SCALE }}" |
159 | | - y="{{ (y - 0.1) * SCALE }}" |
160 | | - font-size="{{ SCALE * 0.75 }}" |
| 158 | + {% elif version.status == "security" %} |
| 159 | + x="{{ (middle_x + end_x) / 2 }}" |
161 | 160 | text-anchor="middle" |
162 | | - > |
163 | | - security |
164 | | - </text> |
165 | | - {% elif version.status == "end-of-life" %} |
166 | | - <text |
167 | | - class="release-cycle-blob-label release-cycle-status-end-of-life" |
168 | | - x="{{ (end_x + 0.25) * SCALE }}" |
| 161 | + {% elif version.status == "end-of-life" %} |
| 162 | + x="{{ end_x + (0.25 * SCALE) }}" |
169 | 163 | text-anchor="start" |
170 | | - y="{{ (y - 0.1) * SCALE }}" |
171 | | - font-size="{{ SCALE * 0.75 }}" |
172 | | - > |
173 | | - end-of-life |
174 | | - </text> |
175 | | - {% else %} |
176 | | - <text |
177 | | - class="release-cycle-blob-label release-cycle-status-feature" |
178 | | - x="{{ (start_x - 0.5) * SCALE }}" |
179 | | - y="{{ (y - 0.1) * SCALE }}" |
180 | | - font-size="{{ SCALE * 0.75 }}" |
| 164 | + {% else %} |
| 165 | + x="{{ start_x - (0.5 * SCALE) }}" |
181 | 166 | text-anchor="end" |
| 167 | + {% endif %} |
182 | 168 | > |
183 | 169 | {{ version.status }} |
184 | 170 | </text> |
185 | | - {% endif %} |
186 | 171 |
|
187 | 172 | <!-- Legend on the left --> |
188 | 173 | <text |
189 | 174 | class="release-cycle-version-label" |
190 | 175 | x="{{ 0.5 * SCALE }}" |
191 | | - y="{{ y * SCALE }}" |
| 176 | + y="{{ version.y * line_height }}" |
192 | 177 | font-size="{{ SCALE }}" |
193 | 178 | > |
194 | 179 | Python {{ version.key }} |
|
198 | 183 | <!-- A line for today --> |
199 | 184 | <line |
200 | 185 | class="release-cycle-today-line" |
201 | | - x1="{{ date_to_x(today) * SCALE }}" |
202 | | - x2="{{ date_to_x(today) * SCALE }}" |
| 186 | + x1="{{ date_to_x(today) }}" |
| 187 | + x2="{{ date_to_x(today) }}" |
203 | 188 | y1="0" |
204 | | - y2="{{ (diagram_height - LINE_HEIGHT) * SCALE }}" |
| 189 | + y2="{{ diagram_height - line_height }}" |
205 | 190 | font-size="{{ SCALE }}" |
206 | 191 | /> |
207 | 192 | </svg> |
0 commit comments