Skip to content

Commit 5413c77

Browse files
committed
Material swatch convention v1.1: bicolor is a diagonal split on the 135° axis (was a vertical 2-sector pie); 3+ hard colours stay a camembert. Doc + French translation, test vectors, reference renderer, glossary, llms.txt
1 parent 27981a3 commit 5413c77

6 files changed

Lines changed: 100 additions & 76 deletions

File tree

‎docs/developers/material-swatch-playground.html‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,12 @@ <h1>The material swatch — reference renderer</h1>
9292
./material-swatch.md — keep the two in step.
9393
═══════════════════════════════════════════════════════════════════════════ */
9494

95-
// N colours, hard edges → N equal conic sectors, first colour at 12 o'clock,
96-
// clockwise. With N = 2 the boundary lands on the vertical axis, which is why
97-
// bicolor is a guaranteed vertical split and not a special case.
95+
// Hard-edged colours. Exactly TWO → a diagonal split on the 135° axis (first
96+
// colour top-left, hard edge at 50 %) — the ramp's hard-edged twin. THREE or
97+
// more → N equal conic sectors, first colour at 12 o'clock, clockwise. One
98+
// function, so every hard split (catalogue list or chip aspect) gets the rule.
9899
const pieSplit = colors => {
100+
if (colors.length === 2) return `linear-gradient(${RAMP_ANGLE}, ${colors[0]} 50%, ${colors[1]} 50%)`;
99101
const step = 360 / colors.length;
100102
return `conic-gradient(${colors.map((c, i) => `${c} ${i * step}deg ${(i + 1) * step}deg`).join(', ')})`;
101103
};
@@ -154,7 +156,7 @@ <h1>The material swatch — reference renderer</h1>
154156
}
155157
if (isBicolor) { // 9
156158
const [c1 = '#cccccc', c2 = '#ffffff'] = slots;
157-
return pieSplit([c1, c2]); // → vertical split
159+
return pieSplit([c1, c2]); // → diagonal split
158160
}
159161
return slots[0] || '#1c2030'; // 10
160162
}
@@ -195,8 +197,8 @@ <h1>The material swatch — reference renderer</h1>
195197
note:'The luminance trap: an unnormalised #000000FF read as "light" puts the black logo on a black spool.',
196198
doc:{ online_color_list:['000000FF'] } },
197199

198-
{ t:'Bicolor — chip aspect 252', rule:'rule 9 — 2 sectors ⇒ vertical split',
199-
note:'Look at all five boxes: the boundary stays vertical on the circle, the square, the banner, the column and the clipped bar. That is what the angular geometry buys.',
200+
{ t:'Bicolor — chip aspect 252', rule:'rule 9 — diagonal split, 135°',
201+
note:'Look at all five boxes: the hard edge runs top-right → bottom-left through the centre of the circle, the square, the banner, the column and the clipped bar — first colour top-left, like the ramp.',
200202
doc:{ color_r:224, color_g:36, color_b:36, color_r2:36, color_g2:99, color_b2:224,
201203
color_r3:0, color_g3:0, color_b3:0, id_aspect2:ASPECT.BICOLOR } },
202204
{ t:'Tricolor — chip aspect 24', rule:'rule 8 — 3 sectors',
@@ -211,7 +213,7 @@ <h1>The material swatch — reference renderer</h1>
211213
{ t:'Declared type: gradient', rule:'rule 2 — ramp 135°',
212214
doc:{ online_color_list:['e02424','f0a020','2463e0'], online_color_type:'gradient' } },
213215
{ t:'Declared type: gradient, TWO colours', rule:'rule 2 — still a ramp',
214-
note:'Deliberately NOT the vertical split: the catalogue asked for a ramp.',
216+
note:'Same axis as the bicolor split, but blended: the catalogue asked for a ramp, not a hard edge.',
215217
doc:{ online_color_list:['e02424','2463e0'], online_color_type:'gradient' } },
216218
{ t:'Declared type: conic_gradient', rule:'rule 1 — catalogue only, never on a chip',
217219
doc:{ online_color_list:['e02424','f0a020','22a06b','2463e0'], online_color_type:'conic_gradient' } },

‎docs/developers/material-swatch.md‎

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,48 @@ protocol has yet to gain. The convention is written against `id_type` in
1313
general: nothing below reads the product type, so a swatch is produced the same
1414
way whatever the material is.
1515

16-
It is normative. A bicolor filament that shows a vertical split in one app and a
17-
left-to-right ramp in another is a bug in whichever one departed from this page
16+
It is normative. A bicolor filament that shows a diagonal split in one app and a
17+
vertical one in another is a bug in whichever one departed from this page
1818
— not a matter of taste. If you cannot reproduce a rule exactly (a platform
1919
with no conic gradient, say), implement the closest equivalent described in
2020
[Non-CSS platforms](#non-css-platforms) and say so; do not invent a different
2121
picture.
2222

23-
- **Convention version:** 1.0
23+
- **Convention version:** 1.1 — bicolor is a **diagonal** split (1.0 split it vertically)
2424
- **Reference renderer:** [`material-swatch-playground.html`](./material-swatch-playground.html) —
2525
open it in any browser, no server, no dependency. Every case, every box shape,
2626
live colour pickers, and the exact CSS it produces.
2727

2828
---
2929

30-
## Two shapes, and only two
30+
## Three shapes, and only three
3131

3232
| Shape | When | Geometry |
3333
|---|---|---|
34-
| **Camembert** (pie) | Every hard-edged material — bicolor, tricolor, any list of N colours | N equal conic sectors, first colour starting at **12 o'clock**, sweeping **clockwise** |
34+
| **Diagonal split** | Exactly **two** hard-edged colours — bicolor | Two halves along the **135°** axis, hard edge through the centre: first colour **top-left**, second **bottom-right** |
35+
| **Camembert** (pie) | **Three or more** hard-edged colours — tricolor, any list of N ≥ 3 | N equal conic sectors, first colour starting at **12 o'clock**, sweeping **clockwise** |
3536
| **Ramp** | Rainbow, and the catalogue's declared `gradient` type | A smooth linear ramp at **135°** — pointing to the bottom-right, so the first colour sits top-left |
3637

37-
Said plainly: **everything is a camembert except a ramp**, and there is exactly
38-
one ramp angle in the whole system.
39-
40-
**Bicolor is the guaranteed vertical split — and it is not a special case.** Two
41-
equal sectors put their boundary on the vertical axis, so a bicolor material shows
42-
a straight vertical edge on *any* box: a round swatch, a square tile, a wide
43-
thumbnail, a partially-filled bar. You get that for free by implementing the
44-
pie; you do not need a separate code path, and you must not introduce one that
45-
draws it mirrored.
46-
47-
Why a pie rather than diagonal bands: sector boundaries are angular, measured
48-
from the box centre, so the picture stays recognisable whatever the box's aspect
49-
ratio. Why 135° for ramps: a ramp has no hard edge to place, so the angle is
50-
free — a diagonal sweep reads as a gradient instead of looking like a
51-
mis-rendered bicolor.
38+
Said plainly: **two hard colours split on the diagonal, more make a camembert,
39+
smooth colours make a ramp** — and there is exactly one angle in the whole system,
40+
135°, shared by the split and the ramp.
41+
42+
**Bicolor is the diagonal split — the ramp's hard-edged twin.** Same axis, same
43+
order (first colour top-left), but a hard edge at 50 % instead of a blend. The
44+
edge runs top-right → bottom-left through the centre of *any* box: a round
45+
swatch, a square tile, a wide thumbnail, a frame around a photo, a
46+
partially-filled bar. Implement it once, in the function that splits a list of
47+
hard colours (N = 2 → diagonal, N ≥ 3 → pie), so every caller gets it; never draw
48+
it mirrored.
49+
50+
Why a diagonal for two colours: a vertical split read as two separate objects
51+
standing side by side — on a thumbnail, and even more on the colour frame round a
52+
product photo, where the photo hid the centre and left a left bar and a right
53+
bar. A diagonal reads as one two-tone material. Why a pie from three colours up:
54+
sector boundaries are angular, measured from the box centre, so N sectors stay
55+
recognisable whatever the box's aspect ratio, where N diagonal bands would not.
56+
Why 135°: it is the one angle of the system — a bicolor and a two-colour ramp
57+
share the axis, and differ only by the hard edge.
5258

5359
---
5460

@@ -74,7 +80,7 @@ authoritative `color_count`):
7480

7581
| id | label | `color_count` | Shape |
7682
|---|---|---|---|
77-
| `252` | Bicolor | 2 | Camembert, 2 sectors → **vertical split** |
83+
| `252` | Bicolor | 2 | **Diagonal split**, 135° |
7884
| `24` | Tricolor | 3 | Camembert, 3 sectors |
7985
| `145` | Rainbow | 3 | Ramp, 135° |
8086

@@ -115,14 +121,14 @@ Let `LIST` = `online_color_list` after [normalisation](#normalisation), `TYPE` =
115121
| # | Condition | Result |
116122
|---|---|---|
117123
| 1 | `LIST ≥ 2` and `TYPE == "conic_gradient"` | Smooth conic sweep, closing on the first colour |
118-
| 2 | `LIST ≥ 2` and `TYPE == "gradient"` | Ramp — **even on two colours**; the catalogue asked for a ramp, so it does not become a bicolor split |
119-
| 3 | `LIST ≥ 2` | **Camembert** of `LIST.length` sectors |
124+
| 2 | `LIST ≥ 2` and `TYPE == "gradient"` | Ramp — **even on two colours**; the catalogue asked for a ramp, so it does not become a hard bicolor split |
125+
| 3 | `LIST ≥ 2` | Hard split of `LIST`: **diagonal** when 2 colours, **camembert** of `LIST.length` sectors when 3 or more |
120126
| 4 | `LIST == 1` | Solid colour — **outranks the chip colour** |
121127
| 5 | aspect **Rainbow** *and* **Tricolor** | Ramp, 3 stops |
122128
| 6 | aspect **Rainbow** *and* **Bicolor** | Ramp, 2 stops |
123129
| 7 | aspect **Rainbow** | Ramp over `SLOTS`; 1 slot → solid; 0 slots → the 6-colour default |
124130
| 8 | aspect **Tricolor** | **Camembert**, 3 sectors over `SLOTS` (slot 3 missing → repeat slot 1) |
125-
| 9 | aspect **Bicolor** | **Camembert**, 2 sectors → **vertical split** |
131+
| 9 | aspect **Bicolor** | **Diagonal split**, 135°, over slots 1 and 2 |
126132
| 10 | otherwise | Solid slot 1; nothing at all → `#1c2030` |
127133

128134
Defaults when an aspect carries no usable colour:
@@ -162,11 +168,11 @@ components are numbers.
162168
With `c1…cN` the normalised colours and `step = 360 / N`:
163169

164170
```css
165-
/* Camembert — rules 3, 8, 9 */
166-
conic-gradient(c1 0deg <step>deg, c2 <step>deg <2·step>deg, …)
171+
/* Diagonal split — exactly two hard colours: rule 3 with 2 colours, rule 9 */
172+
linear-gradient(135deg, c1 50%, c2 50%)
167173

168-
/* Bicolor is that same expression with N = 2 — the guaranteed vertical split */
169-
conic-gradient(#e02424 0deg 180deg, #2463e0 180deg 360deg)
174+
/* Camembert — three or more hard colours: rule 3 with ≥ 3 colours, rule 8 */
175+
conic-gradient(c1 0deg <step>deg, c2 <step>deg <2·step>deg, …)
170176

171177
/* Ramp — rules 2, 5, 6, 7. One angle for every ramp in the system. */
172178
linear-gradient(135deg, c1, c2, …)
@@ -186,10 +192,10 @@ Any implementation must reproduce these exactly.
186192
|---|---|
187193
| `{online_color_list:["FF5722"]}` | `#FF5722` |
188194
| `{online_color_list:["000000FF"]}` | `#000000` |
189-
| `{online_color_list:["e02424","2463e0"]}` | `conic-gradient(#e02424 0deg 180deg, #2463e0 180deg 360deg)` |
195+
| `{online_color_list:["e02424","2463e0"]}` | `linear-gradient(135deg, #e02424 50%, #2463e0 50%)` |
190196
| `{online_color_list:["e02424","2463e0","22a06b"]}` | `conic-gradient(#e02424 0deg 120deg, #2463e0 120deg 240deg, #22a06b 240deg 360deg)` |
191197
| `{online_color_list:["e02424","2463e0"],online_color_type:"gradient"}` | `linear-gradient(135deg, #e02424, #2463e0)` |
192-
| `{color_r:224,color_g:36,color_b:36,color_r2:36,color_g2:99,color_b2:224,id_aspect2:252}` | `conic-gradient(#e02424 0deg 180deg, #2463e0 180deg 360deg)` |
198+
| `{color_r:224,color_g:36,color_b:36,color_r2:36,color_g2:99,color_b2:224,id_aspect2:252}` | `linear-gradient(135deg, #e02424 50%, #2463e0 50%)` |
193199
| `{id_aspect1:145}` | `linear-gradient(135deg, #ff0000, #ff8800, #ffff00, #00cc00, #0000ff, #8b00ff)` |
194200
| `{}` | `#1c2030` |
195201

@@ -229,7 +235,7 @@ alpha and expand shorthand before computing.
229235

230236
## Non-CSS platforms
231237

232-
Flutter, SwiftUI, Android or any canvas renderer implements the same two shapes.
238+
Flutter, SwiftUI, Android or any canvas renderer implements the same three shapes.
233239
Angles are always measured the CSS way: **0° points up, angles increase
234240
clockwise.**
235241

@@ -238,6 +244,10 @@ clockwise.**
238244
`SweepGradient` with `transform: GradientRotation(-pi/2)`. Do not approximate
239245
it with pie slices drawn as paths unless the tile is square — the sector
240246
boundaries must follow the box.
247+
- **Diagonal split** — the ramp's geometry with a hard edge at the middle:
248+
`c1@0, c1@0.5, c2@0.5, c2@1` from the **top-left** to the **bottom-right**.
249+
Flutter: `LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight,
250+
colors: [c1, c1, c2, c2], stops: [0, .5, .5, 1])`.
241251
- **Ramp** — a linear gradient from the **top-left** corner to the
242252
**bottom-right**. Flutter: `Alignment.topLeft → Alignment.bottomRight`.
243253
- **Hard stops** — repeat each colour at both ends of its band

‎docs/glossary.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ owns the full story.
3434
| **Twin Tag** | The two chips of one spool, written together as a pair and kept identical for the spool's whole life — always counted as **one** spool. |
3535
| **Reference database** | The shared ID tables (brands, materials, aspects, types, diameters, units) served from `cdn.tigertag.io`, so a chip encoded by one tool reads identically in every other. See [Universal filament identity](./concepts/universal-filament-identity.md). |
3636
| **Aspect** | The reference-database field describing a filament's colour and finish; the [material swatch](./developers/material-swatch.md) is painted from it. |
37-
| **Material swatch** | The normative convention for turning a spool's colour *data* into the same *picture* on every surface — two shapes only, with a reference renderer to check an implementation against. See [The material swatch](./developers/material-swatch.md). |
37+
| **Material swatch** | The normative convention for turning a spool's colour *data* into the same *picture* on every surface — three shapes only (diagonal split, camembert, ramp), with a reference renderer to check an implementation against. See [The material swatch](./developers/material-swatch.md). |
3838
| **`.ttag` file** | The portable, offline interchange file carrying one or more inventory materials (TigerData, TigerData+, TigerTag or TigerTag+) between tools. See [the `.ttag` format](./developers/ttag-format.md). |
3939
| **Second life** | Re-encoding a chip for a refilled or re-purposed spool, or converting it to plain NDEF for any other NFC use — a chip should never become e-waste. See [Second life](./philosophy/second-life.md). |
4040
| **Smartphone bridge** | Using a phone (or a desktop reader) to identify a spool for a printer that has no RFID reader of its own. See [The smartphone bridge](./philosophy/smartphone-bridge.md). |

0 commit comments

Comments
 (0)