Verbeter anatomie en plaatsing van avataraccessoires (v0.4.46-beta) #1

Open
bes-r wants to merge 192 commits from bes-r/avatar-realism into main AGit
4 changed files with 118 additions and 45 deletions
Showing only changes of commit 6832043470 - Show all commits

View file

@ -1 +1 @@
0.4.47-beta 0.4.48-beta

View file

@ -147,45 +147,91 @@ const AVATAR_HAIR_MARKUP = {
none: { back: () => "", front: () => "" }, none: { back: () => "", front: () => "" },
tuft: { tuft: {
back: () => "", back: () => "",
front: (c) => `<path class="av-part av-hair av-hair-tuft" d="M 40 8 C 34 0 40 -8 48 0 C 49 -10 59 -9 58 1 C 66 -5 72 3 64 10 C 56 6 48 6 40 8 Z" fill="${c}" stroke="${avatarShade(c,-0.35)}" stroke-width="1.3" stroke-linejoin="round"/>`, front: (c) => `<g class="av-part av-hair av-hair-tuft" fill="${c}" stroke="${avatarShade(c,-0.4)}" stroke-linejoin="round">
<path d="M 37 11 C 39 5 42 2 47 1 C 43 -3 45 -9 50 -11 C 52 -6 53 -2 52 2 C 56 -3 62 -3 65 1 C 61 2 59 5 58 8 C 53 6 47 7 42 12 Z" stroke-width="1.25"/>
<path class="av-hair-texture" d="M 48 0 Q 50 4 48 7 M 56 1 Q 54 4 54 7" fill="none" stroke="${avatarShade(c,0.28)}" stroke-width="1.15" stroke-linecap="round"/>
</g>`,
}, },
spiky: { spiky: {
back: () => "", back: () => "",
front: (c) => `<path class="av-part av-hair av-hair-spiky" d="M 18 21 C 18 8 22 1 28 -3 L 27 -11 L 38 -3 L 44 -13 L 51 -3 L 62 -13 L 65 -2 L 78 -9 L 76 3 C 81 7 83 14 82 22 C 68 11 32 11 18 21 Z" fill="${c}" stroke="${avatarShade(c,-0.35)}" stroke-width="1.3" stroke-linejoin="round"/>`, front: (c) => `<g class="av-part av-hair av-hair-spiky" fill="${c}" stroke="${avatarShade(c,-0.4)}" stroke-linejoin="round">
<path d="M 18 22 C 18 12 21 5 27 1 L 28 -7 L 36 -2 L 41 -11 L 48 -3 L 55 -13 L 61 -3 L 71 -9 L 70 0 L 79 -3 L 76 7 C 80 11 82 16 82 22 C 70 16 62 14 51 14 C 39 14 29 17 18 22 Z" stroke-width="1.35"/>
<path class="av-hairline" d="M 20 20 C 35 13 65 11 80 20" fill="none" stroke="${avatarShade(c,-0.18)}" stroke-width="1.2" stroke-linecap="round"/>
<path class="av-hair-texture" d="M 31 2 L 35 9 M 43 -3 L 45 8 M 57 -4 L 55 8 M 69 0 L 64 10" fill="none" stroke="${avatarShade(c,0.25)}" stroke-width="1.05" stroke-linecap="round"/>
</g>`,
}, },
curly: { curly: {
back: () => "", back: (c) => `<path class="av-part av-hair-back av-hair-curly-back" d="M 13 21 C 11 12 16 5 23 2 C 22 -6 31 -11 38 -8 C 43 -16 55 -16 61 -9 C 69 -12 78 -6 77 2 C 85 5 89 14 86 23 C 83 31 77 35 72 38 L 28 38 C 21 35 15 30 13 21 Z" fill="${avatarShade(c,-0.08)}" stroke="${avatarShade(c,-0.42)}" stroke-width="1.35"/>`,
front: (c) => `<g class="av-part av-hair av-hair-curly" fill="${c}" stroke="${avatarShade(c,-0.32)}" stroke-width="1.1"> front: (c) => `<g class="av-part av-hair av-hair-curly" fill="${c}" stroke="${avatarShade(c,-0.34)}" stroke-width="1.05">
<circle cx="19" cy="16" r="8"/><circle cx="27" cy="5" r="9"/><circle cx="39" cy="-1" r="10"/> <circle cx="19" cy="18" r="7.2"/><circle cx="23" cy="8" r="7.7"/><circle cx="31" cy="1" r="8.2"/>
<circle cx="51" cy="-3" r="10.5"/><circle cx="64" cy="0" r="10"/><circle cx="76" cy="7" r="9"/><circle cx="82" cy="18" r="8"/> <circle cx="41" cy="-3" r="8.5"/><circle cx="51" cy="-4" r="8.8"/><circle cx="61" cy="-2" r="8.4"/>
<circle cx="70" cy="2" r="8.1"/><circle cx="78" cy="9" r="7.7"/><circle cx="82" cy="19" r="7.1"/>
<path class="av-hairline" d="M 18 21 C 25 18 27 13 29 10 C 34 16 40 15 43 10 C 48 16 54 16 58 10 C 63 15 70 16 73 11 C 75 16 78 19 82 21" fill="none" stroke="${avatarShade(c,-0.3)}" stroke-width="1.3" stroke-linecap="round"/>
<path class="av-hair-texture" d="M 28 3 Q 34 -1 38 4 M 46 -5 Q 52 -1 55 -6 M 64 0 Q 70 3 72 8" fill="none" stroke="${avatarShade(c,0.3)}" stroke-width="1.15" stroke-linecap="round"/>
</g>`, </g>`,
}, },
wavy: { wavy: {
back: (c) => `<path class="av-part av-hair-back av-hair-wavy" d="M 15 17 C 14 -2 30 -11 50 -11 C 71 -11 87 1 86 23 C 88 37 84 50 76 58 C 76 48 70 43 73 33 C 62 42 37 42 26 33 C 29 44 23 50 23 58 C 15 48 12 34 15 17 Z" fill="${c}" stroke="${avatarShade(c,-0.35)}" stroke-width="1.4"/>`, back: (c) => `<path class="av-part av-hair-back av-hair-wavy" d="M 14 19 C 13 0 28 -12 49 -12 C 71 -12 86 1 86 22 C 88 31 85 39 81 45 C 85 51 82 58 75 62 C 76 54 70 50 72 43 C 66 48 61 51 57 52 L 43 52 C 37 50 32 47 28 43 C 30 51 24 55 25 62 C 17 58 14 51 18 44 C 14 37 12 28 14 19 Z" fill="${avatarShade(c,-0.07)}" stroke="${avatarShade(c,-0.42)}" stroke-width="1.4" stroke-linejoin="round"/>`,
front: (c) => `<path class="av-part av-hair av-hair-wavy-fringe" d="M 17 19 C 17 2 31 -8 50 -8 C 69 -8 82 2 83 19 C 76 15 74 9 68 9 C 66 17 58 16 54 10 C 49 18 40 18 37 10 C 31 16 24 17 17 19 Z" fill="${c}" stroke="${avatarShade(c,-0.35)}" stroke-width="1.2"/>`, front: (c) => `<g class="av-part av-hair av-hair-wavy-fringe" fill="${c}" stroke="${avatarShade(c,-0.38)}" stroke-linejoin="round">
<path d="M 17 21 C 17 2 31 -9 50 -9 C 69 -9 82 2 83 20 C 76 18 71 14 67 9 C 63 15 58 18 52 19 C 47 20 42 17 40 12 C 34 17 26 20 17 21 Z" stroke-width="1.3"/>
<path class="av-side-lock av-side-lock-left" d="M 18 18 C 16 25 18 31 23 35 C 21 29 25 25 28 20 Z" stroke-width="1.15"/>
<path class="av-side-lock av-side-lock-right" d="M 82 17 C 85 25 82 31 77 35 C 79 29 75 25 72 20 Z" stroke-width="1.15"/>
<path class="av-hair-part" d="M 57 -5 C 53 2 48 8 40 12" fill="none" stroke="${avatarShade(c,0.35)}" stroke-width="1.15" stroke-linecap="round"/>
<path class="av-hair-texture" d="M 29 2 Q 34 6 31 11 M 69 1 Q 73 6 70 11" fill="none" stroke="${avatarShade(c,0.24)}" stroke-width="1" stroke-linecap="round"/>
</g>`,
}, },
bob: { bob: {
back: (c) => `<path class="av-part av-hair-back av-hair-bob" d="M 13 18 C 13 -2 29 -12 50 -12 C 72 -12 87 0 87 21 L 84 59 L 70 58 L 67 36 L 33 36 L 30 58 L 16 59 Z" fill="${c}" stroke="${avatarShade(c,-0.38)}" stroke-width="1.4" stroke-linejoin="round"/>`, back: (c) => `<path class="av-part av-hair-back av-hair-bob" d="M 14 20 C 14 0 29 -12 50 -12 C 71 -12 86 0 86 21 C 87 34 85 47 81 57 C 77 61 72 61 67 58 L 66 42 C 61 46 57 48 50 48 C 43 48 38 46 33 42 L 32 58 C 27 61 21 61 17 57 C 14 46 13 33 14 20 Z" fill="${avatarShade(c,-0.06)}" stroke="${avatarShade(c,-0.42)}" stroke-width="1.4"/>`,
front: (c) => `<path class="av-part av-hair av-hair-bob-fringe" d="M 16 20 C 17 1 31 -8 50 -8 C 69 -8 82 2 84 20 C 72 18 68 12 64 7 C 58 14 51 17 43 17 C 35 17 28 14 22 20 Z" fill="${c}" stroke="${avatarShade(c,-0.35)}" stroke-width="1.2"/>`, front: (c) => `<g class="av-part av-hair av-hair-bob-fringe" fill="${c}" stroke="${avatarShade(c,-0.38)}" stroke-linejoin="round">
<path d="M 16 21 C 16 2 31 -9 50 -9 C 69 -9 83 2 84 21 C 76 19 69 15 63 8 C 58 13 53 17 47 18 C 39 20 31 17 24 13 C 23 17 21 20 16 21 Z" stroke-width="1.3"/>
<path class="av-side-lock av-side-lock-left" d="M 17 18 C 15 28 17 39 23 47 C 27 44 29 39 28 32 C 25 27 23 23 24 15 Z" stroke-width="1.15"/>
<path class="av-side-lock av-side-lock-right" d="M 83 18 C 85 28 83 39 77 47 C 73 44 71 39 72 32 C 75 27 77 23 76 15 Z" stroke-width="1.15"/>
<path class="av-hair-part" d="M 62 -4 C 59 2 58 5 63 8" fill="none" stroke="${avatarShade(c,0.34)}" stroke-width="1.15" stroke-linecap="round"/>
<path class="av-hair-texture" d="M 24 23 Q 21 34 24 40 M 76 23 Q 79 34 76 40" fill="none" stroke="${avatarShade(c,0.24)}" stroke-width="1" stroke-linecap="round"/>
</g>`,
}, },
ponytail: { ponytail: {
back: (c) => `<path class="av-part av-hair-back av-hair-ponytail" d="M 72 5 C 91 3 99 16 94 31 C 91 44 82 54 73 59 C 80 45 78 32 70 23 Z" fill="${c}" stroke="${avatarShade(c,-0.38)}" stroke-width="1.4"/><ellipse cx="75" cy="10" rx="5" ry="4" fill="${avatarShade(c,-0.25)}"/>`, back: (c) => `<g class="av-part av-hair-back av-hair-ponytail" stroke="${avatarShade(c,-0.42)}" stroke-linejoin="round">
front: (c) => `<path class="av-part av-hair av-hair-ponytail-front" d="M 16 20 C 17 0 31 -10 50 -10 C 69 -10 82 1 84 20 C 68 11 32 11 16 20 Z" fill="${c}" stroke="${avatarShade(c,-0.35)}" stroke-width="1.3"/>`, <path d="M 72 8 C 83 1 94 7 96 18 C 99 32 91 47 78 58 C 82 45 80 34 74 29 C 86 31 88 19 76 17 Z" fill="${avatarShade(c,-0.06)}" stroke-width="1.4"/>
<path class="av-hair-texture" d="M 83 13 C 92 22 89 36 83 45" fill="none" stroke="${avatarShade(c,0.22)}" stroke-width="1.15" stroke-linecap="round"/>
<ellipse class="av-hair-tie" cx="75" cy="13" rx="5.2" ry="4.2" fill="#d84f78" stroke="#96304f" stroke-width="1"/>
</g>`,
front: (c) => `<g class="av-part av-hair av-hair-ponytail-front" fill="${c}" stroke="${avatarShade(c,-0.38)}" stroke-linejoin="round">
<path d="M 16 21 C 17 1 31 -10 50 -10 C 68 -10 80 0 83 17 C 76 16 70 13 65 8 C 57 15 47 18 38 16 C 31 15 25 16 16 21 Z" stroke-width="1.3"/>
<path class="av-hair-part" d="M 63 -5 C 61 1 61 5 65 8" fill="none" stroke="${avatarShade(c,0.34)}" stroke-width="1.15" stroke-linecap="round"/>
<path class="av-sideburn" d="M 18 19 C 17 25 19 29 23 32 C 22 26 25 22 28 18 Z" stroke-width="1.05"/>
</g>`,
}, },
braids: { braids: {
back: (c) => `<g class="av-part av-hair-back av-hair-braids" fill="${c}" stroke="${avatarShade(c,-0.38)}" stroke-width="1"> back: (c) => `<g class="av-part av-hair-back av-hair-braids" fill="${c}" stroke="${avatarShade(c,-0.42)}" stroke-width="1.05">
<path d="M 22 18 C 11 31 15 45 13 58 C 12 65 17 72 22 67 C 26 62 20 57 23 49 C 27 38 30 27 22 18 Z"/> <path d="M 22 17 C 15 24 15 31 20 36 C 14 40 14 47 19 51 C 14 56 16 64 21 68 C 27 65 28 58 23 53 C 29 49 28 42 23 38 C 29 32 28 23 22 17 Z"/>
<path d="M 78 18 C 89 31 85 45 87 58 C 88 65 83 72 78 67 C 74 62 80 57 77 49 C 73 38 70 27 78 18 Z"/> <path d="M 78 17 C 85 24 85 31 80 36 C 86 40 86 47 81 51 C 86 56 84 64 79 68 C 73 65 72 58 77 53 C 71 49 72 42 77 38 C 71 32 72 23 78 17 Z"/>
<path d="M 15 38 L 23 42 M 14 51 L 22 55 M 85 38 L 77 42 M 86 51 L 78 55" fill="none" stroke="${avatarShade(c,0.2)}" stroke-width="2"/> <path class="av-hair-texture" d="M 18 31 L 25 35 M 17 45 L 25 49 M 18 58 L 24 62 M 82 31 L 75 35 M 83 45 L 75 49 M 82 58 L 76 62" fill="none" stroke="${avatarShade(c,0.26)}" stroke-width="1.55" stroke-linecap="round"/>
<path class="av-hair-tie" d="M 17 66 Q 21 70 25 66 M 75 66 Q 79 70 83 66" fill="none" stroke="#d84f78" stroke-width="2.3" stroke-linecap="round"/>
</g>`,
front: (c) => `<g class="av-part av-hair av-hair-braids-front" fill="${c}" stroke="${avatarShade(c,-0.38)}" stroke-linejoin="round">
<path d="M 16 21 C 17 1 31 -10 50 -10 C 69 -10 82 1 84 21 C 76 19 70 14 65 8 C 60 14 54 17 48 18 C 39 19 31 16 23 12 C 22 17 20 19 16 21 Z" stroke-width="1.3"/>
<path class="av-hair-part" d="M 63 -5 C 61 1 61 5 65 8" fill="none" stroke="${avatarShade(c,0.34)}" stroke-width="1.15" stroke-linecap="round"/>
<path class="av-sideburn" d="M 18 18 C 17 25 19 29 23 32 C 22 26 25 22 27 18 Z M 82 18 C 83 25 81 29 77 32 C 78 26 75 22 73 18 Z" stroke-width="1.05"/>
</g>`, </g>`,
front: (c) => `<path class="av-part av-hair av-hair-braids-front" d="M 16 20 C 17 0 31 -10 50 -10 C 69 -10 82 1 84 20 C 75 17 69 12 64 7 C 58 14 50 17 42 16 C 34 15 28 12 22 20 Z" fill="${c}" stroke="${avatarShade(c,-0.35)}" stroke-width="1.3"/>`,
}, },
mohawk: { mohawk: {
back: () => "", back: () => "",
front: (c) => `<path class="av-part av-hair av-hair-mohawk" d="M 40 13 C 38 3 39 -7 45 -13 L 50 -5 L 56 -15 C 62 -6 62 4 59 14 C 53 10 46 10 40 13 Z" fill="${c}" stroke="${avatarShade(c,-0.38)}" stroke-width="1.3" stroke-linejoin="round"/>`, front: (c) => `<g class="av-part av-hair av-hair-mohawk" fill="${c}" stroke="${avatarShade(c,-0.42)}" stroke-linejoin="round">
<path d="M 41 14 C 40 8 40 2 42 -3 L 45 -13 L 50 -6 L 55 -16 L 59 -6 L 62 -11 L 60 2 C 60 7 59 11 58 15 C 53 12 47 12 41 14 Z" stroke-width="1.35"/>
<path class="av-hair-texture" d="M 47 -9 L 48 8 M 55 -10 L 53 8" fill="none" stroke="${avatarShade(c,0.28)}" stroke-width="1.1" stroke-linecap="round"/>
</g>`,
}, },
}; };
/* Iedere kop gebruikt dezelfde haarankers, met een minimale correctie voor
het asymmetrische dinosaurusprofiel. De wrapper maakt de pasvorm expliciet
en houdt alle lokken als een laag bij elkaar. */
function avatarHairLayer(markup, species, layer){
if(!markup) return "";
const transform = species === "dino" ? "translate(1 0)" : species === "monster" ? "translate(0 -1)" : "translate(0 0)";
return `<g class="av-hair-layer av-hair-${layer}-layer av-hair-fit-${species}" transform="${transform}">${markup}</g>`;
}
const AVATAR_HEAD_ACC_MARKUP = { const AVATAR_HEAD_ACC_MARKUP = {
none: () => "", none: () => "",
cap: () => ` cap: () => `
@ -328,9 +374,9 @@ function renderAvatarFace(config, size){
const bodyAccessory = AVATAR_BODY_ACC_MARKUP[c.accessoryBody]; const bodyAccessory = AVATAR_BODY_ACC_MARKUP[c.accessoryBody];
const hair = AVATAR_HAIR_MARKUP[c.hairStyle]; const hair = AVATAR_HAIR_MARKUP[c.hairStyle];
let markup = bodyAccessory.back(); let markup = bodyAccessory.back();
markup += hair.back(c.hairColor); markup += avatarHairLayer(hair.back(c.hairColor), c.species, "back");
markup += avatarBodyMarkup(c.species, c.bodyColor); markup += avatarBodyMarkup(c.species, c.bodyColor);
if(!avatarHairSuppressed(c)) markup += hair.front(c.hairColor); if(!avatarHairSuppressed(c)) markup += avatarHairLayer(hair.front(c.hairColor), c.species, "front");
markup += AVATAR_HEAD_ACC_MARKUP[c.accessoryHead](); markup += AVATAR_HEAD_ACC_MARKUP[c.accessoryHead]();
markup += bodyAccessory.front(); markup += bodyAccessory.front();
svg.innerHTML = markup; svg.innerHTML = markup;

View file

@ -2,7 +2,7 @@
"use strict"; "use strict";
/* version shown until /api/version resolves (or if the fetch fails, e.g. offline). /* version shown until /api/version resolves (or if the fetch fails, e.g. offline).
Kept in sync by hand with the VERSION file at the repo root on every release. */ Kept in sync by hand with the VERSION file at the repo root on every release. */
const VERSION = "0.4.47-beta"; const VERSION = "0.4.48-beta";
(function(){ (function(){
const tag = document.getElementById("verTag"); const tag = document.getElementById("verTag");
tag.textContent = "v"+VERSION; tag.textContent = "v"+VERSION;

View file

@ -58,34 +58,61 @@ test('avatars combineren een grote kop met een compact soortspecifiek lijf',asyn
assert.notEqual(bodies.monster,bodies.animal); assert.notEqual(bodies.monster,bodies.animal);
}); });
test('haar heeft natuurlijke voor- en achterlagen en vier nieuwe stijlen',async()=>{ test("haar volgt de kop met natuurlijke details en correcte lagen",async()=>{
const api=await loadAvatarRenderer(); const api=await loadAvatarRenderer();
assert.deepEqual(Array.from(api.hairIds),['none','tuft','spiky','curly','wavy','bob','ponytail','braids','mohawk']); assert.deepEqual(Array.from(api.hairIds),["none","tuft","spiky","curly","wavy","bob","ponytail","braids","mohawk"]);
for(const id of api.hairIds){ for(const id of api.hairIds){
assert.equal(typeof api.hair[id].back,'function',id+' mist haar-achterlaag'); assert.equal(typeof api.hair[id].back,"function",id+" mist haar-achterlaag");
assert.equal(typeof api.hair[id].front,'function',id+' mist haar-voorlaag'); assert.equal(typeof api.hair[id].front,"function",id+" mist haar-voorlaag");
}
for(const id of ['tuft','wavy','bob','braids']){
const markup=api.renderAvatarFace({...base,hairStyle:id,hairColor:'#123456'},'lg').innerHTML;
assert.ok(markup.includes('av-hair-'+id),id+' wordt niet getekend');
assert.ok(markup.includes('#123456'),id+' gebruikt de gekozen haarkleur niet');
} }
const bob=api.renderAvatarFace({...base,hairStyle:'bob'},'lg').innerHTML; for(const id of api.hairIds.filter(id=>id!=="none")){
assert.ok(bob.indexOf('av-hair-bob')<bob.indexOf('av-head-large')); const front=api.hair[id].front("#123456");
assert.ok(bob.indexOf('av-hair-bob-fringe')>bob.indexOf('av-head-large')); const back=api.hair[id].back("#123456");
const braids=api.renderAvatarFace({...base,hairStyle:'braids'},'lg').innerHTML; const combined=back+front;
assert.ok(braids.indexOf('av-hair-braids')<braids.indexOf('av-head-large')); assert.ok(front.includes("av-hair-"+id),id+" mist een herkenbare voorlaag");
assert.ok(braids.indexOf('av-hair-braids-front')>braids.indexOf('av-head-large')); assert.ok(combined.includes("av-hair-texture"),id+" mist natuurlijke haartextuur");
assert.doesNotMatch(combined,/undefined|NaN/,id+" bevat ongeldige SVG-waarden");
}
for(const id of ["curly","wavy","bob","ponytail","braids"]){
assert.notEqual(api.hair[id].back("#123456"),"",id+" mist volume achter de kop");
}
for(const id of ["tuft","spiky","mohawk"]){
assert.equal(api.hair[id].back("#123456"),"",id+" hoort geen onzichtbare achterlaag te hebben");
}
for(const id of ["wavy","bob","ponytail","braids"]){
assert.ok(api.hair[id].front("#123456").includes("av-hair-part"),id+" mist een natuurlijke scheiding");
}
assert.ok(api.hair.wavy.front("#123456").includes("av-side-lock"));
assert.ok(api.hair.bob.front("#123456").includes("av-side-lock"));
assert.ok(api.hair.ponytail.back("#123456").includes("av-hair-tie"));
assert.ok(api.hair.braids.back("#123456").includes("av-hair-tie"));
const cap=api.renderAvatarFace({...base,hairStyle:'spiky',hairColor:'#123456',accessoryHead:'cap'},'lg').innerHTML; const transforms={dino:"translate(1 0)",monster:"translate(0 -1)",animal:"translate(0 0)"};
const beanie=api.renderAvatarFace({...base,hairStyle:'spiky',hairColor:'#123456',accessoryHead:'beanie'},'lg').innerHTML; for(const [species,transform] of Object.entries(transforms)){
const crown=api.renderAvatarFace({...base,hairStyle:'spiky',hairColor:'#123456',accessoryHead:'crown'},'lg').innerHTML; const markup=api.renderAvatarFace({...base,species,hairStyle:"wavy",hairColor:"#123456"},"lg").innerHTML;
const cappedBraids=api.renderAvatarFace({...base,hairStyle:'braids',hairColor:'#123456',accessoryHead:'cap'},'lg').innerHTML; assert.equal((markup.match(new RegExp("av-hair-fit-"+species,"g"))||[]).length,2,species+" mist voor- of achterpasvorm");
assert.ok(!cap.includes('#123456'),'pet hoort de voorste haarlaag af te dekken'); assert.ok(markup.includes(`transform="${transform}"`),species+" gebruikt een verkeerde kruincorrectie");
assert.ok(!beanie.includes('#123456'),'muts hoort de voorste haarlaag af te dekken'); assert.ok(markup.indexOf("av-hair-back-layer")<markup.indexOf("av-head-large"));
assert.ok(crown.includes('#123456'),'kroon hoort op het haar te rusten'); assert.ok(markup.indexOf("av-hair-front-layer")>markup.indexOf("av-head-large"));
assert.ok(cappedBraids.includes('#123456'),'vlechten horen naast een pet zichtbaar te blijven'); }
const bob=api.renderAvatarFace({...base,hairStyle:"bob"},"lg").innerHTML;
assert.ok(bob.indexOf("av-hair-bob")<bob.indexOf("av-head-large"));
assert.ok(bob.indexOf("av-hair-bob-fringe")>bob.indexOf("av-head-large"));
const braids=api.renderAvatarFace({...base,hairStyle:"braids"},"lg").innerHTML;
assert.ok(braids.indexOf("av-hair-braids")<braids.indexOf("av-head-large"));
assert.ok(braids.indexOf("av-hair-braids-front")>braids.indexOf("av-head-large"));
const cap=api.renderAvatarFace({...base,hairStyle:"spiky",hairColor:"#123456",accessoryHead:"cap"},"lg").innerHTML;
const beanie=api.renderAvatarFace({...base,hairStyle:"spiky",hairColor:"#123456",accessoryHead:"beanie"},"lg").innerHTML;
const crown=api.renderAvatarFace({...base,hairStyle:"spiky",hairColor:"#123456",accessoryHead:"crown"},"lg").innerHTML;
const cappedBraids=api.renderAvatarFace({...base,hairStyle:"braids",hairColor:"#123456",accessoryHead:"cap"},"lg").innerHTML;
assert.ok(!cap.includes("av-hair-front-layer"),"pet hoort kort haar af te dekken");
assert.ok(!beanie.includes("av-hair-front-layer"),"muts hoort kort haar af te dekken");
assert.ok(crown.includes("av-hair-front-layer"),"kroon hoort op het haar te rusten");
assert.ok(cappedBraids.includes("av-hair-back-layer"),"vlechten horen naast een pet zichtbaar te blijven");
assert.ok(!cappedBraids.includes("av-hair-front-layer"),"pet hoort de bovenkant van vlechten af te dekken");
}); });
test('accessoires gebruiken vaste ankers en correcte voor- en achterlagen',async()=>{ test('accessoires gebruiken vaste ankers en correcte voor- en achterlagen',async()=>{