Skip to content

Commit 87f4f35

Browse files
committed
Material swatch v1.2: bicolor and tricolor are a soft split — solid colours with a 20 % blended seam on the 135° axis, not a full ramp. Doc + French translation, test vectors, reference renderer, glossary, llms.txt
1 parent bfdcdd5 commit 87f4f35

6 files changed

Lines changed: 134 additions & 95 deletions

File tree

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

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

95-
// Hard-edged colours. TWO or THREE → a smooth 135° ramp, no hard edge, first
96-
// colour top-left — same shape as a declared gradient of the same length.
95+
// Hard-edged colours. TWO or THREE → a 135° soft split: each colour solid, a
96+
// SPLIT_BLEND % seam blended at each boundary, first colour top-left.
9797
// FOUR or more → N equal conic sectors, first colour at 12 o'clock, clockwise
98-
// (a ramp through 4+ colours turns to mud, sectors stay recognisable). One
98+
// (4+ bands on one diagonal get too thin, sectors stay recognisable). One
9999
// function, so every hard split (catalogue list or chip aspect) gets the rule.
100100
const pieSplit = colors => {
101-
if (colors.length <= 3) return `linear-gradient(${RAMP_ANGLE}, ${colors.join(', ')})`;
101+
if (colors.length <= 3) {
102+
// Soft split: each colour solid, a SPLIT_BLEND % seam blended at each boundary.
103+
const seg = 100 / colors.length, h = SPLIT_BLEND / 2;
104+
const stops = colors.map((c, i) => `${c} ${+Math.max(0, i * seg + (i ? h : 0)).toFixed(2)}% ${+Math.min(100, (i + 1) * seg - (i < colors.length - 1 ? h : 0)).toFixed(2)}%`);
105+
return `linear-gradient(${RAMP_ANGLE}, ${stops.join(', ')})`;
106+
}
102107
const step = 360 / colors.length;
103108
return `conic-gradient(${colors.map((c, i) => `${c} ${i * step}deg ${(i + 1) * step}deg`).join(', ')})`;
104109
};
105110
// One angle for every smooth ramp in the system.
106111
const RAMP_ANGLE = '135deg';
112+
const SPLIT_BLEND = 20; // % of the diagonal blended at each soft-split boundary
107113

108114
// Aspect ids that change the shape. Match on the ID, never on the label.
109115
const ASPECT = { BICOLOR: 252, TRICOLOR: 24, RAINBOW: 145 };
@@ -153,11 +159,11 @@ <h1>The material swatch — reference renderer</h1>
153159
}
154160
if (isTricolor) { // 8
155161
const [c1 = '#cccccc', c2 = '#888888', c3] = slots;
156-
return pieSplit(c3 ? [c1, c2, c3] : [c1, c2]); // slot 3 missing → 2-colour ramp
162+
return pieSplit(c3 ? [c1, c2, c3] : [c1, c2]); // slot 3 missing → 2-colour soft split
157163
}
158164
if (isBicolor) { // 9
159165
const [c1 = '#cccccc', c2 = '#ffffff'] = slots;
160-
return pieSplit([c1, c2]); // → ramp
166+
return pieSplit([c1, c2]); // → soft split
161167
}
162168
return slots[0] || '#1c2030'; // 10
163169
}
@@ -198,25 +204,25 @@ <h1>The material swatch — reference renderer</h1>
198204
note:'The luminance trap: an unnormalised #000000FF read as "light" puts the black logo on a black spool.',
199205
doc:{ online_color_list:['000000FF'] } },
200206

201-
{ t:'Bicolor — chip aspect 252', rule:'rule 9 — ramp, 135°',
202-
note:'Look at all five boxes: the blend runs top-left → bottom-right across the circle, the square, the banner, the column and the clipped bar, first colour top-left, no hard edge.',
207+
{ t:'Bicolor — chip aspect 252', rule:'rule 9 — soft split, 135°',
208+
note:'Look at all five boxes: the seam 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; each colour stays solid, only a 20 % seam blends.',
203209
doc:{ color_r:224, color_g:36, color_b:36, color_r2:36, color_g2:99, color_b2:224,
204210
color_r3:0, color_g3:0, color_b3:0, id_aspect2:ASPECT.BICOLOR } },
205-
{ t:'Tricolor — chip aspect 24', rule:'rule 8 — ramp, 135°',
211+
{ t:'Tricolor — chip aspect 24', rule:'rule 8 — soft split, 135°',
206212
doc:{ color_r:224, color_g:36, color_b:36, color_r2:36, color_g2:99, color_b2:224,
207213
color_r3:34, color_g3:160, color_b3:107, id_aspect2:ASPECT.TRICOLOR } },
208214

209-
{ t:'Two colours in the list', rule:'rule 3 — ramp',
215+
{ t:'Two colours in the list', rule:'rule 3 — soft split',
210216
doc:{ online_color_list:['e02424','2463e0'] } },
211-
{ t:'Three colours in the list', rule:'rule 3 — still a ramp',
217+
{ t:'Three colours in the list', rule:'rule 3 — still a soft split',
212218
doc:{ online_color_list:['e02424','2463e0','22a06b'] } },
213219
{ t:'Four colours in the list (multi)', rule:'rule 3 — camembert from here up',
214220
doc:{ online_color_list:['e02424','f0a020','2463e0','22a06b'], online_color_type:'multi' } },
215221

216222
{ t:'Declared type: gradient', rule:'rule 2 — ramp 135°',
217223
doc:{ online_color_list:['e02424','f0a020','2463e0'], online_color_type:'gradient' } },
218-
{ t:'Declared type: gradient, TWO colours', rule:'rule 2 — same shape as an undeclared 2-colour list',
219-
note:'The declared type only changes anything once the list reaches 4 colours — below that, a hard list already renders as this same ramp.',
224+
{ t:'Declared type: gradient, TWO colours', rule:'rule 2 — a smooth ramp, not a soft split',
225+
note:'The catalogue asked for a gradient, so it blends all the way — compare with the undeclared two-colour list, which holds each colour solid.',
220226
doc:{ online_color_list:['e02424','2463e0'], online_color_type:'gradient' } },
221227
{ t:'Declared type: conic_gradient', rule:'rule 1 — catalogue only, never on a chip',
222228
doc:{ online_color_list:['e02424','f0a020','22a06b','2463e0'], online_color_type:'conic_gradient' } },

‎docs/developers/material-swatch.md‎

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ 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 smooth ramp in one app and a
17-
hard diagonal split in another is a bug in whichever one departed from this page
16+
It is normative. A bicolor filament that shows a soft split in one app and a
17+
hard edge or a full ramp 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.2 — bicolor and tricolor are a **135° ramp** (1.1 drew bicolor as a
24-
hard diagonal split and tricolor as a camembert; 1.0 split bicolor vertically)
23+
- **Convention version:** 1.2 — bicolor and tricolor are a **135° soft split**: solid colours with a
24+
20 % blended seam (1.1 drew bicolor as a hard diagonal split and tricolor as a camembert; 1.0
25+
split bicolor vertically)
2526
- **Reference renderer:** [`material-swatch-playground.html`](./material-swatch-playground.html) —
2627
open it in any browser, no server, no dependency. Every case, every box shape,
2728
live colour pickers, and the exact CSS it produces.
@@ -32,28 +33,34 @@ picture.
3233

3334
| Shape | When | Geometry |
3435
|---|---|---|
35-
| **Ramp** | **Two or three** hard-edged colours — bicolor, tricolor — plus rainbow and the catalogue's declared `gradient` type | A smooth linear ramp at **135°**, no hard edge — pointing to the bottom-right, so the first colour sits top-left |
36+
| **Soft split** | **Two or three** hard-edged colours — bicolor, tricolor | N equal bands along the **135°** axis, first colour **top-left**; each colour stays **solid**, and only a seam of **20 %** of the diagonal (`SPLIT_BLEND`) centred on each boundary blends |
37+
| **Ramp** | Rainbow, and the catalogue's declared `gradient` type | A smooth linear ramp at **135°**, evenly spaced stops — pointing to the bottom-right, so the first colour sits top-left |
3638
| **Camembert** (pie) | **Four or more** hard-edged colours — any list of N ≥ 4 | N equal conic sectors, first colour starting at **12 o'clock**, sweeping **clockwise** |
3739

38-
Said plainly: **two or three hard colours blend into a ramp, four or more make a
39-
camembert** — and there is exactly one angle in the whole system, 135°, shared
40-
by every ramp in it.
40+
Said plainly: **two or three hard colours make a soft split, four or more make a
41+
camembert, smooth colours make a ramp** — and there is exactly one angle in the
42+
whole system, 135°, shared by the soft split and the ramp.
4143

42-
**Bicolor and tricolor are a ramp — the same shape as a declared gradient.**
43-
Same axis, same order (first colour top-left), no hard edge anywhere: 2 or 3
44-
hard colours blend exactly like a catalogue `gradient` of the same length.
44+
**Bicolor and tricolor are a soft split — the ramp's axis, the split's clarity.**
45+
Same axis and order as a ramp (first colour top-left), but each colour holds
46+
solid over its band and only a **20 %** seam blends at each boundary. With
47+
`seg = 100 / N` and `h = SPLIT_BLEND / 2 = 10`, colour *i* holds from
48+
`i·seg + h` (0 for the first) to `(i+1)·seg − h` (100 for the last).
4549
Implement it once, in the function that splits a list of colours (N ≤ 3 →
46-
ramp, N ≥ 4 → pie), so every caller gets it; never draw it mirrored.
47-
48-
Why a ramp for two or three colours: a hard edge still read as two or three
49-
separate objects — on a thumbnail, and even more on the colour frame round a
50-
product photo, where the photo hid the centre and left disjoint coloured bars.
51-
A ramp reads as one multi-tone material on any box shape. Why a pie from four
52-
colours up: a ramp through four or more colours turns to mud, while sector
50+
soft split, N ≥ 4 → pie), so every caller gets it; never draw it mirrored.
51+
52+
Why a soft split for two or three colours: a hard edge still read as two or
53+
three separate objects — on a thumbnail, and even more on the colour frame
54+
round a product photo, where the photo hid the centre and left disjoint
55+
coloured bars. A full ramp fixed that but washed the colours out, so neither
56+
reads as the true colour. The soft split keeps each colour solid and blends only
57+
the seam: one multi-tone material, true colours, on any box shape. 20 % was
58+
chosen by eye across every surface (a wider seam leaves a tricolor's middle
59+
colour almost no solid band). Why a pie from four
60+
colours up: four or more bands along one diagonal get too thin to read, while sector
5361
boundaries stay angular and recognisable — measured from the box centre, so N
5462
sectors stay legible whatever the box's aspect ratio. Why 135°: it is the one
55-
angle of the system, shared by every ramp, whether it has 2, 3 or more smooth
56-
stops.
63+
angle of the system, shared by the soft split and every ramp.
5764

5865
---
5966

@@ -79,8 +86,8 @@ authoritative `color_count`):
7986

8087
| id | label | `color_count` | Shape |
8188
|---|---|---|---|
82-
| `252` | Bicolor | 2 | **Ramp**, 135° |
83-
| `24` | Tricolor | 3 | **Ramp**, 135° |
89+
| `252` | Bicolor | 2 | **Soft split**, 135° |
90+
| `24` | Tricolor | 3 | **Soft split**, 135° |
8491
| `145` | Rainbow | 3 | Ramp, 135° |
8592

8693
Every other aspect (`Silk`, `Matt`, `Glitter`, …) has `color_count` ≤ 1 and does
@@ -120,14 +127,14 @@ Let `LIST` = `online_color_list` after [normalisation](#normalisation), `TYPE` =
120127
| # | Condition | Result |
121128
|---|---|---|
122129
| 1 | `LIST ≥ 2` and `TYPE == "conic_gradient"` | Smooth conic sweep, closing on the first colour |
123-
| 2 | `LIST ≥ 2` and `TYPE == "gradient"` | Ramp — same shape `LIST` would get at 2-3 colours anyway; the declared type only matters once `LIST.length ≥ 4` |
124-
| 3 | `LIST ≥ 2` | **Ramp** of `LIST` when 2 or 3 colours, **camembert** of `LIST.length` sectors when 4 or more |
130+
| 2 | `LIST ≥ 2` and `TYPE == "gradient"` | Ramp — even on two or three colours; the catalogue asked for a smooth ramp, so it does not become a soft split |
131+
| 3 | `LIST ≥ 2` | **Soft split** of `LIST` when 2 or 3 colours, **camembert** of `LIST.length` sectors when 4 or more |
125132
| 4 | `LIST == 1` | Solid colour — **outranks the chip colour** |
126133
| 5 | aspect **Rainbow** *and* **Tricolor** | Ramp, 3 stops |
127134
| 6 | aspect **Rainbow** *and* **Bicolor** | Ramp, 2 stops |
128135
| 7 | aspect **Rainbow** | Ramp over `SLOTS`; 1 slot → solid; 0 slots → the 6-colour default |
129-
| 8 | aspect **Tricolor** | **Ramp**, 135°, over `SLOTS` (slot 3 missing → a 2-colour ramp over slots 1 and 2, it does not repeat slot 1) |
130-
| 9 | aspect **Bicolor** | **Ramp**, 135°, over slots 1 and 2 |
136+
| 8 | aspect **Tricolor** | **Soft split**, 135°, over `SLOTS` (slot 3 missing → a 2-colour soft split over slots 1 and 2, it does not repeat slot 1) |
137+
| 9 | aspect **Bicolor** | **Soft split**, 135°, over slots 1 and 2 |
131138
| 10 | otherwise | Solid slot 1; nothing at all → `#1c2030` |
132139

133140
Defaults when an aspect carries no usable colour:
@@ -137,7 +144,7 @@ Defaults when an aspect carries no usable colour:
137144
| Rainbow, no colours | `#ff0000 #ff8800 #ffff00 #00cc00 #0000ff #8b00ff` |
138145
| Rainbow + Tricolor | `#ff4d4d #ffd93d #4da3ff` |
139146
| Rainbow + Bicolor | `#ff7a00 #8a2be2` |
140-
| Tricolor | `#cccccc #888888` (slot 3 missing → 2-colour ramp, slot 1 is not repeated) |
147+
| Tricolor | `#cccccc #888888` (slot 3 missing → 2-colour soft split, slot 1 is not repeated) |
141148
| Bicolor | `#cccccc #ffffff` |
142149
| Nothing | `#1c2030` |
143150

@@ -164,11 +171,16 @@ components are numbers.
164171

165172
## The exact expressions
166173

167-
With `c1…cN` the normalised colours and `step = 360 / N`:
174+
With `c1…cN` the normalised colours, `step = 360 / N`, `seg = 100 / N` and
175+
`SPLIT_BLEND = 20`:
168176

169177
```css
170-
/* Ramp — 2 or 3 hard colours: rule 3 with 2-3 colours, rules 5-9. Same shape
171-
as a declared gradient, one angle for every ramp in the system. */
178+
/* Soft split — 2 or 3 hard colours: rule 3 with 2-3 colours, rules 8-9.
179+
Each colour solid over its band, a 20 % blended seam at each boundary. */
180+
linear-gradient(135deg, c1 0% 40%, c2 60% 100%) /* N = 2 */
181+
linear-gradient(135deg, c1 0% 23.33%, c2 43.33% 56.67%, c3 76.67% 100%) /* N = 3 */
182+
183+
/* Ramp — rainbow and a declared gradient: rules 2, 5-7 */
172184
linear-gradient(135deg, c1, c2, …)
173185

174186
/* Camembert — four or more hard colours: rule 3 with ≥ 4 colours */
@@ -189,11 +201,11 @@ Any implementation must reproduce these exactly.
189201
|---|---|
190202
| `{online_color_list:["FF5722"]}` | `#FF5722` |
191203
| `{online_color_list:["000000FF"]}` | `#000000` |
192-
| `{online_color_list:["e02424","2463e0"]}` | `linear-gradient(135deg, #e02424, #2463e0)` |
193-
| `{online_color_list:["e02424","2463e0","22a06b"]}` | `linear-gradient(135deg, #e02424, #2463e0, #22a06b)` |
204+
| `{online_color_list:["e02424","2463e0"]}` | `linear-gradient(135deg, #e02424 0% 40%, #2463e0 60% 100%)` |
205+
| `{online_color_list:["e02424","2463e0","22a06b"]}` | `linear-gradient(135deg, #e02424 0% 23.33%, #2463e0 43.33% 56.67%, #22a06b 76.67% 100%)` |
194206
| `{online_color_list:["e02424","2463e0","22a06b","f0a020"]}` | `conic-gradient(#e02424 0deg 90deg, #2463e0 90deg 180deg, #22a06b 180deg 270deg, #f0a020 270deg 360deg)` |
195207
| `{online_color_list:["e02424","2463e0"],online_color_type:"gradient"}` | `linear-gradient(135deg, #e02424, #2463e0)` |
196-
| `{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, #2463e0)` |
208+
| `{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 0% 40%, #2463e0 60% 100%)` |
197209
| `{id_aspect1:145}` | `linear-gradient(135deg, #ff0000, #ff8800, #ffff00, #00cc00, #0000ff, #8b00ff)` |
198210
| `{}` | `#1c2030` |
199211

@@ -242,10 +254,14 @@ clockwise.**
242254
`SweepGradient` with `transform: GradientRotation(-pi/2)`. Do not approximate
243255
it with pie slices drawn as paths unless the tile is square — the sector
244256
boundaries must follow the box. Only for **four or more** hard colours.
245-
- **Ramp** — a linear gradient from the **top-left** corner to the
246-
**bottom-right**, evenly spaced stops, no hard edge. Used for **two or
247-
three** hard colours (bicolor, tricolor), for rainbow, and for the
248-
catalogue's declared `gradient` type. Flutter: `Alignment.topLeft →
257+
- **Soft split** — a linear gradient from the **top-left** corner to the
258+
**bottom-right** with the stops above: each colour held solid, a 20 % seam
259+
blended at each boundary. Used for **two or three** hard colours (bicolor,
260+
tricolor). Flutter: `LinearGradient(begin: Alignment.topLeft, end:
261+
Alignment.bottomRight)` with `colors` doubled and `stops` `[0, .4, .6, 1]`
262+
(N = 2) or `[0, .2333, .4333, .5667, .7667, 1]` (N = 3).
263+
- **Ramp** — the same axis, evenly spaced stops, no hold. Used for rainbow and
264+
for the catalogue's declared `gradient` type. Flutter: `Alignment.topLeft →
249265
Alignment.bottomRight`.
250266

251267
---

‎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 (ramp for 2-3 hard colours, camembert for 4+), 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 (a 135° soft split for 2-3 hard colours, a camembert for 4+, a 135° ramp for smooth colours), 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)