@broberg/bodymap 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/dist/three.cjs +6 -3
- package/dist/three.cjs.map +1 -1
- package/dist/three.d.cts +16 -0
- package/dist/three.d.ts +16 -0
- package/dist/three.js +6 -3
- package/dist/three.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -211,6 +211,47 @@ had *arrived* on a device that never *showed* it.)
|
|
|
211
211
|
`VIBRATION_PATTERNS.ignore` is empty on purpose: a tap that changed nothing must
|
|
212
212
|
not feel like it did.
|
|
213
213
|
|
|
214
|
+
## `seam` — soft region edges are OPT-IN, and here is why
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
<BodyMap3D seam /> // soft colour transition between regions
|
|
218
|
+
<BodyMap3D /> // default: flat, full-contrast regions
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Default off, because the default has to be the accessible one.** The blend mixes
|
|
222
|
+
the *body* colour into a marked region near its boundary — and on a **small** mark,
|
|
223
|
+
the boundary is most of the mark.
|
|
224
|
+
|
|
225
|
+
Measured on a phone (`iphone-15`), a consumer's real palette, one knee marked,
|
|
226
|
+
averaged over every pixel of the mark:
|
|
227
|
+
|
|
228
|
+
| | mark colour | contrast vs body |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| `seam` on | `rgb(114, 58, 79)` | **3.59:1** — fails WCAG AA |
|
|
231
|
+
| `seam` off (default) | `rgb(111, 22, 56)` | **4.71:1** — passes |
|
|
232
|
+
|
|
233
|
+
At the single strongest pixel the cost is small (5.12 → 4.78), which is why a
|
|
234
|
+
spot-check misses it. The average is what a person sees.
|
|
235
|
+
|
|
236
|
+
**Turn it on** when marked areas are large and you have no accessibility duty — it
|
|
237
|
+
genuinely looks better there. **Leave it off** for anything a public authority,
|
|
238
|
+
a clinician or a partially-sighted user will read.
|
|
239
|
+
|
|
240
|
+
## Smooth skin: we do not recompute the model's normals
|
|
241
|
+
|
|
242
|
+
If your `.glb` ships normals, the renderer uses them. It only calls
|
|
243
|
+
`computeVertexNormals()` when the file has none.
|
|
244
|
+
|
|
245
|
+
This matters more than it sounds. `computeVertexNormals()` averages face normals
|
|
246
|
+
**per vertex index**, and a body mesh has split vertices along its UV seams — two
|
|
247
|
+
copies at the same position averaging different faces. The two sides then light
|
|
248
|
+
differently, drawing a visible crease along every seam: a line down the torso, and
|
|
249
|
+
across the chest, waist, shoulders, knees, elbows and ankles. The figure looks
|
|
250
|
+
assembled from parts. Measured side by side: with the call, every line is there;
|
|
251
|
+
without it, none are.
|
|
252
|
+
|
|
253
|
+
If you supply your own model, **ship it with normals.**
|
|
254
|
+
|
|
214
255
|
## Colour control — `BodymapPalette`
|
|
215
256
|
|
|
216
257
|
Both renderers theme off one palette (consumer-defined):
|
package/dist/three.cjs
CHANGED
|
@@ -308,6 +308,7 @@ function BodyMap3D(props) {
|
|
|
308
308
|
showRegionCode = true,
|
|
309
309
|
onFeedback,
|
|
310
310
|
haptics,
|
|
311
|
+
seam = false,
|
|
311
312
|
className
|
|
312
313
|
} = props;
|
|
313
314
|
const chrome = uiColors(palette);
|
|
@@ -345,6 +346,8 @@ function BodyMap3D(props) {
|
|
|
345
346
|
const pickRef = react.useRef(() => "ignore");
|
|
346
347
|
const setReadyRef = react.useRef(setReady);
|
|
347
348
|
setReadyRef.current = setReady;
|
|
349
|
+
const seamRef = react.useRef(seam);
|
|
350
|
+
seamRef.current = seam;
|
|
348
351
|
const apiRef = react.useRef(null);
|
|
349
352
|
react.useEffect(() => {
|
|
350
353
|
if (!webglAvailable()) {
|
|
@@ -438,7 +441,7 @@ function BodyMap3D(props) {
|
|
|
438
441
|
};
|
|
439
442
|
for (let i = 0; i < vertexRegion.length; i++) {
|
|
440
443
|
tmp.copy(col(vertexRegion[i]));
|
|
441
|
-
const w = vertexBlend[i];
|
|
444
|
+
const w = seamRef.current ? vertexBlend[i] : 0;
|
|
442
445
|
if (w > 0) {
|
|
443
446
|
tmp2.copy(col(vertexNeighbour[i]));
|
|
444
447
|
tmp.lerp(tmp2, w);
|
|
@@ -472,7 +475,7 @@ function BodyMap3D(props) {
|
|
|
472
475
|
});
|
|
473
476
|
if (bodyMesh) {
|
|
474
477
|
const geo = bodyMesh.geometry;
|
|
475
|
-
geo.computeVertexNormals();
|
|
478
|
+
if (!geo.getAttribute("normal")) geo.computeVertexNormals();
|
|
476
479
|
const pos = geo.getAttribute("position");
|
|
477
480
|
const n = pos.count;
|
|
478
481
|
const cols = new Float32Array(n * 3);
|
|
@@ -609,7 +612,7 @@ function BodyMap3D(props) {
|
|
|
609
612
|
}, []);
|
|
610
613
|
react.useEffect(() => {
|
|
611
614
|
apiRef.current?.refresh();
|
|
612
|
-
}, [selected, report, palette]);
|
|
615
|
+
}, [selected, report, palette, seam]);
|
|
613
616
|
const sexInited = react.useRef(false);
|
|
614
617
|
react.useEffect(() => {
|
|
615
618
|
if (!sexInited.current) {
|