fix: maak kleurplaatverf zichtbaar en herteken de atlas (v0.4.75-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 34s

Kwast en verfemmer werkten al, maar het resultaat was onzichtbaar: de
contourlaag zette fill="#fff" op elk vak en vertrouwde op
mix-blend-mode:multiply om dat onschadelijk te maken. Doordat .coloring-ink
zelf een stacking context is (position+z-index), vormt die een geisoleerde
blendgroep zonder achtergrond, en werd het wit gewoon dekkend over de hele
tekening gelegd. Live nagemeten met headless Chromium.

- Tekenmodel omgezet naar een echte schildersvolgorde in een enkele SVG: elk
  vak wordt uitgeklapt naar vak -> verflaag -> contour, zodat verf zichtbaar
  blijft en latere vakken de verf en contour van eerdere vakken afdekken.
  Daarmee kijk je ook niet meer door figuren heen.
- Canvas vervangen door SVG-streken: geen opschalingsonscherpte meer en geen
  donkere naden (dekking staat per streek op de groep, niet per segment).
- Kwast blijft binnen de lijnen als hulp, gestuurd door niveau: groep 1-4 op
  niveau 1 en 2, groep 5-8 op niveau 1, daarboven vrij schilderen.
- Verfemmer dekt een vak volledig; de gum pelt de bovenste laag af (eerst de
  vulling, daarna de streken eronder).
- Kleurwijzer toegevoegd: op niveau 1 laat een stip per vak de voorgestelde
  kleur zien, op niveau 2 op te vragen met een knop, op niveau 3 uit.
- Niveau sloopt de tekening niet meer: data-tier staat alleen op groepen en
  detaillijnen, dus een rekwisiet verdwijnt als geheel en de plaat is op elk
  niveau een complete tekening. Niveau 3 voegt alleen fijn lijnwerk toe.
- Atlas hertekend naar 20 platen, 5 per groepenband en 1 per thema. Twee
  lijndiktes, vakken op maat van de band, geen evenodd-gaten, en composities
  met voor-, midden- en achtergrond.
- partBelow/elementFromPoint en het omzetten van pointer-events vervallen:
  aanwijzen gebeurt meetkundig met isPointInFill/isPointInStroke.
- langchange-listener ruimt zichzelf op na demontage.
- Staat bewaart nu streken in plaats van een PNG; oud werk met een ink-PNG
  blijft zichtbaar.
- Tests: asserties die de bug vastzetten verwijderd en vervangen door een
  regressietest die verbiedt dat CSS een fill op een kleurvak zet, plus
  controles op gesloten vakken, geen transform, tier-regels, vakaantallen per
  niveau en de clip-matrix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YErxhic5w9K57zmt8Q1uS
This commit is contained in:
Ramon 2026-07-30 00:39:14 +02:00
parent 66d6cf4d46
commit c6b3a14923
9 changed files with 1418 additions and 1026 deletions

View file

@ -1 +1 @@
0.4.74-beta 0.4.75-beta

View file

@ -1,4 +1,10 @@
/* Thematische kleurplaten */ /* Thematische kleurplaten
Er staat maar één laag op het papier: de SVG van de plaat. De tekenvolgorde
is de DOM-volgorde (vakken -> verf -> contouren -> kleurwijzer), dus hier
staat geen z-index-stapel en geen mix-blend-mode meer. Belangrijk: dit
bestand zet nooit een `fill` op een kleurvak - dat zou van de vulling van de
verfemmer winnen en de verf onzichtbaar maken. */
.coloring-app{--coloring-ink:#1c293d;--coloring-detail:#2d3e56;height:100%;min-height:0;display:flex;flex-direction:column;background:linear-gradient(180deg,color-mix(in srgb,var(--orange) 4%,var(--surface)),var(--surface));color:var(--ink)} .coloring-app{--coloring-ink:#1c293d;--coloring-detail:#2d3e56;height:100%;min-height:0;display:flex;flex-direction:column;background:linear-gradient(180deg,color-mix(in srgb,var(--orange) 4%,var(--surface)),var(--surface));color:var(--ink)}
.coloring-choices,.coloring-tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 11px;border-bottom:1px solid color-mix(in srgb,var(--line) 72%,var(--orange));background:linear-gradient(100deg,color-mix(in srgb,var(--orange) 10%,var(--surface)),color-mix(in srgb,var(--purple) 5%,var(--surface)))} .coloring-choices,.coloring-tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 11px;border-bottom:1px solid color-mix(in srgb,var(--line) 72%,var(--orange));background:linear-gradient(100deg,color-mix(in srgb,var(--orange) 10%,var(--surface)),color-mix(in srgb,var(--purple) 5%,var(--surface)))}
.coloring-choices label{display:grid;grid-template-columns:auto minmax(120px,1fr);align-items:center;gap:6px;color:var(--muted);font-size:11px;font-weight:850} .coloring-choices label{display:grid;grid-template-columns:auto minmax(120px,1fr);align-items:center;gap:6px;color:var(--muted);font-size:11px;font-weight:850}
@ -13,41 +19,42 @@
.coloring-tools button.on{background:var(--surface);box-shadow:0 2px 7px rgba(30,50,90,.16)} .coloring-tools button.on{background:var(--surface);box-shadow:0 2px 7px rgba(30,50,90,.16)}
.coloring-tool-buttons button.on{outline:2px solid var(--orange);outline-offset:-2px} .coloring-tool-buttons button.on{outline:2px solid var(--orange);outline-offset:-2px}
.coloring-color i{width:23px;height:23px;display:block;border:1px solid rgba(28,41,61,.25);border-radius:50%;box-shadow:inset 0 2px 3px rgba(255,255,255,.42),inset 0 -2px 3px rgba(28,41,61,.12),0 1px 2px rgba(28,41,61,.12);transition:transform .14s ease} .coloring-color i{width:23px;height:23px;display:block;border:1px solid rgba(28,41,61,.25);border-radius:50%;box-shadow:inset 0 2px 3px rgba(255,255,255,.42),inset 0 -2px 3px rgba(28,41,61,.12),0 1px 2px rgba(28,41,61,.12);transition:transform .14s ease}
.coloring-color.on{outline:2.5px solid var(--coloring-ink);outline-offset:-3px}.coloring-color.on i{transform:scale(1.08)} .coloring-color.on{outline:2.5px solid var(--coloring-ink,#1c293d);outline-offset:-3px}.coloring-color.on i{transform:scale(1.08)}
.coloring-size i{display:block;border-radius:50%;background:var(--ink)} .coloring-size i{display:block;border-radius:50%;background:var(--ink)}
.coloring-size.on{outline:2px solid var(--orange);outline-offset:-2px} .coloring-size.on{outline:2px solid var(--orange);outline-offset:-2px}
.coloring-custom{display:flex;align-items:center;gap:5px;color:var(--muted);font-size:10px;font-weight:800} .coloring-custom{display:flex;align-items:center;gap:5px;color:var(--muted);font-size:10px;font-weight:800}
.coloring-custom input{width:36px;height:36px;padding:3px;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:0 2px 7px rgba(30,50,90,.08);cursor:pointer} .coloring-custom input{width:36px;height:36px;padding:3px;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:0 2px 7px rgba(30,50,90,.08);cursor:pointer}
.coloring-tools .coloring-reset{margin-left:auto;background:var(--red-soft);color:var(--red);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--red) 18%,transparent)} .coloring-tools .coloring-guide-btn{margin-left:auto;background:color-mix(in srgb,var(--teal) 14%,var(--surface));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--teal) 20%,transparent)}
.coloring-tools .coloring-guide-btn.on{outline:2px solid var(--teal);outline-offset:-2px}
.coloring-tools .coloring-reset{background:var(--red-soft);color:var(--red);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--red) 18%,transparent)}
.coloring-tools .coloring-done{width:auto;min-width:112px;padding:0 14px;color:#fff;box-shadow:0 4px 10px color-mix(in srgb,var(--orange) 24%,transparent);font-size:13px} .coloring-tools .coloring-done{width:auto;min-width:112px;padding:0 14px;color:#fff;box-shadow:0 4px 10px color-mix(in srgb,var(--orange) 24%,transparent);font-size:13px}
.coloring-stage{flex:1;min-height:0;overflow:auto;display:grid;place-items:center;padding:18px;background:radial-gradient(circle at 12% 18%,color-mix(in srgb,var(--orange) 13%,transparent),transparent 31%),radial-gradient(circle at 88% 82%,color-mix(in srgb,var(--purple) 10%,transparent),transparent 34%),linear-gradient(145deg,var(--surface-2),color-mix(in srgb,var(--purple) 5%,var(--surface)))} .coloring-stage{flex:1;min-height:0;overflow:auto;display:grid;place-items:center;padding:18px;background:radial-gradient(circle at 12% 18%,color-mix(in srgb,var(--orange) 13%,transparent),transparent 31%),radial-gradient(circle at 88% 82%,color-mix(in srgb,var(--purple) 10%,transparent),transparent 34%),linear-gradient(145deg,var(--surface-2),color-mix(in srgb,var(--purple) 5%,var(--surface)))}
.coloring-paper{position:relative;width:min(100%,900px);aspect-ratio:4/3;flex:none;overflow:hidden;border:2px solid rgba(255,255,255,.94);border-radius:22px;background:#fff;box-shadow:inset 0 0 0 1px rgba(28,41,61,.12),0 4px 0 rgba(28,41,61,.05),0 20px 46px rgba(30,50,90,.18);isolation:isolate} /* het papier vangt alle aanwijzing op; binnen de SVG wordt meetkundig
.coloring-art,.coloring-paper canvas,.coloring-ink{position:absolute;inset:0;width:100%;height:100%} aangewezen met isPointInFill/isPointInStroke */
.coloring-art{z-index:1}.coloring-paper canvas{z-index:2}.coloring-ink{z-index:3;pointer-events:none} .coloring-paper{position:relative;width:min(100%,900px);aspect-ratio:4/3;flex:none;overflow:hidden;border:2px solid rgba(255,255,255,.94);border-radius:22px;background:#fff;box-shadow:inset 0 0 0 1px rgba(28,41,61,.12),0 4px 0 rgba(28,41,61,.05),0 20px 46px rgba(30,50,90,.18);touch-action:none;cursor:crosshair}
.coloring-art svg,.coloring-ink svg{width:100%;height:100%;display:block}.coloring-art svg{background:#fff}.coloring-ink svg{position:absolute;inset:0;overflow:visible} /* de plaat draagt haar eigen inktkleuren, zodat een losse SVG (cast, export,
.coloring-art [hidden],.coloring-ink [hidden]{display:none} testharnas) er hetzelfde uitziet als in de widget */
.coloring-svg-lines{shape-rendering:geometricPrecision} .coloring-sheet{--coloring-ink:#1c293d;--coloring-detail:#2d3e56;position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none;shape-rendering:geometricPrecision}
.coloring-svg-lines .coloring-region{paint-order:fill stroke;stroke-width:4.8;stroke-opacity:1} .coloring-sheet [hidden]{display:none}
.coloring-svg-lines .coloring-region[data-role="backdrop"]{stroke-width:2.8} /* het vak zelf draagt alleen kleur; de contour is een eigen vorm erboven */
.coloring-svg-lines .coloring-region[data-level="2"]{stroke-width:4.4} .coloring-region{stroke:none}
.coloring-svg-lines .coloring-region[data-level="3"]{stroke-width:4} .coloring-legacy-ink{opacity:.85}
.coloring-svg-lines .coloring-detail{stroke:var(--coloring-detail);stroke-width:2.4;stroke-opacity:.94;pointer-events:none} /* verf: één groep per streek, dekking op de groep zodat er binnen een streek
.coloring-svg-lines .coloring-detail[data-level="3"]{stroke-width:2.05;stroke-opacity:.9} geen donkere naden ontstaan */
.coloring-svg-lines .coloring-recognition{display:none} .coloring-stroke{pointer-events:none}
.coloring-ink-overlay{pointer-events:none;shape-rendering:geometricPrecision;mix-blend-mode:multiply} .coloring-app[data-tool="eraser"] .coloring-stroke{pointer-events:stroke}
.coloring-ink-svg .coloring-region{fill:#fff!important;stroke:var(--coloring-ink);stroke-width:4.8;stroke-opacity:1;paint-order:fill stroke} /* contouren: twee lijndiktes, zodat voor- en achtergrond leesbaar blijven */
.coloring-ink-svg .coloring-region[data-role="backdrop"]{stroke-width:2.8} .coloring-outline{stroke:var(--coloring-ink,#1c293d);stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2}
.coloring-ink-svg .coloring-region[data-level="2"]{stroke-width:4.4} .coloring-outline.coloring-contour{stroke-width:6.4}
.coloring-ink-svg .coloring-region[data-level="3"]{stroke-width:4} .coloring-outline.coloring-flat{stroke-width:3.4}
.coloring-ink-svg .coloring-detail{stroke:var(--coloring-detail);stroke-width:2.4;stroke-opacity:.94} .coloring-outline.coloring-backdrop{stroke-width:2.4}
.coloring-ink-svg .coloring-detail[data-level="3"]{stroke-width:2.05;stroke-opacity:.9} .coloring-detail{stroke:var(--coloring-detail,#2d3e56);stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
.coloring-paper canvas{touch-action:none;cursor:crosshair} .coloring-solid{fill:var(--coloring-ink,#1c293d);stroke:none}
.coloring-app[data-tool="brush"] .coloring-paper canvas{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Ctext y='25' font-size='24'%3E%F0%9F%96%8C%EF%B8%8F%3C/text%3E%3C/svg%3E") 5 25,crosshair} .coloring-guide-dot{stroke:#fff;stroke-width:3}
.coloring-app[data-tool="bucket"] .coloring-paper canvas{pointer-events:none}
.coloring-app[data-tool="bucket"] .coloring-art{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Ctext y='24' font-size='23'%3E%F0%9F%AA%A3%3C/text%3E%3C/svg%3E") 6 24,pointer}
.coloring-app[data-tool="bucket"] .coloring-art [data-fillable]{pointer-events:all}
.coloring-app[data-tool="bucket"] .coloring-art [data-fillable]:hover{fill-opacity:.82}
.coloring-hint{min-height:30px;padding:7px 11px;color:var(--muted);font-size:11px;font-weight:700;text-align:center;background:var(--surface)} .coloring-hint{min-height:30px;padding:7px 11px;color:var(--muted);font-size:11px;font-weight:700;text-align:center;background:var(--surface)}
.coloring-app[data-tool="brush"] .coloring-paper{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Ctext y='25' font-size='24'%3E%F0%9F%96%8C%EF%B8%8F%3C/text%3E%3C/svg%3E") 5 25,crosshair}
.coloring-app[data-tool="bucket"] .coloring-paper{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Ctext y='24' font-size='23'%3E%F0%9F%AA%A3%3C/text%3E%3C/svg%3E") 6 24,pointer}
.coloring-app[data-tool="eraser"] .coloring-paper{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Ctext y='24' font-size='23'%3E%F0%9F%A7%BD%3C/text%3E%3C/svg%3E") 6 24,pointer}
.widget[data-widget="coloring"]{--widget-accent:var(--orange)} .widget[data-widget="coloring"]{--widget-accent:var(--orange)}
@media(max-width:620px){ @media(max-width:620px){
@ -59,7 +66,7 @@
.coloring-tool-buttons button{min-width:36px;width:36px;padding:0}.coloring-tool-label{display:none} .coloring-tool-buttons button{min-width:36px;width:36px;padding:0}.coloring-tool-label{display:none}
.coloring-color i{width:18px;height:18px} .coloring-color i{width:18px;height:18px}
.coloring-custom span{display:none}.coloring-custom input{width:32px;height:32px} .coloring-custom span{display:none}.coloring-custom input{width:32px;height:32px}
.coloring-tools .coloring-reset{margin-left:0} .coloring-tools .coloring-guide-btn{margin-left:0}
.coloring-tools .coloring-done{min-width:90px;font-size:11px} .coloring-tools .coloring-done{min-width:90px;font-size:11px}
.coloring-stage{place-items:start center;padding:8px} .coloring-stage{place-items:start center;padding:8px}
.coloring-paper{min-width:360px;border-radius:10px} .coloring-paper{min-width:360px;border-radius:10px}

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.74-beta"; const VERSION = "0.4.75-beta";
/* Safari en oudere mobiele browsers behandelen 100vh/100% soms als de hoogte /* Safari en oudere mobiele browsers behandelen 100vh/100% soms als de hoogte
achter hun adres- en navigatiebalk. visualViewport geeft de werkelijk achter hun adres- en navigatiebalk. visualViewport geeft de werkelijk
zichtbare hoogte; de CSS gebruikt deze pixelwaarde als betrouwbare bron en zichtbare hoogte; de CSS gebruikt deze pixelwaarde als betrouwbare bron en
@ -53,7 +53,10 @@ const I18N = {
colorTheme:"Thema", colorPage:"Kleurplaat", colorProfile:"Groep {year} · niveau {level}", colorTheme:"Thema", colorPage:"Kleurplaat", colorProfile:"Groep {year} · niveau {level}",
colorThemeAll:"Alle thema's", colorThemeAnimals:"Dieren", colorThemeSeasons:"Seizoenen", colorThemeNature:"Natuur", colorThemeScience:"Wetenschap & techniek", colorThemeCulture:"Cultuur & omgeving", colorThemeAll:"Alle thema's", colorThemeAnimals:"Dieren", colorThemeSeasons:"Seizoenen", colorThemeNature:"Natuur", colorThemeScience:"Wetenschap & techniek", colorThemeCulture:"Cultuur & omgeving",
colorToolBrush:"Kwast", colorToolBucket:"Verfemmer", colorToolBrush:"Kwast", colorToolBucket:"Verfemmer",
colorBrushHint:"Schilder vloeiende streken met de kwast; de contourlijnen blijven altijd zichtbaar.", colorBucketHint:"Tik op een omsloten vak om het in één keer te kleuren.", colorEraserHint:"Veeg kwaststreken weg of tik en sleep over een gevuld vak om ook de verfpotkleur te wissen.", colorBrushHint:"Schilder vloeiende streken met de kwast; de contourlijnen blijven altijd zichtbaar.", colorBucketHint:"Tik op een omsloten vak om het in één keer te kleuren; de emmer dekt het kwastwerk in dat vak af.", colorEraserHint:"De gum haalt de bovenste laag weg: eerst de vulling van een vak, daarna de kwaststreken die eronder zaten.",
colorGuide:"Kleurhulp", colorLevelHelp1:"Grote vakken, en je kwast blijft netjes binnen de lijnen. De stippen laten zien welke kleur past.",
colorLevelHelp2:"Meer vakken om in te kleuren. Zet de kleurhulp aan als je een tip wilt.",
colorLevelHelp3:"Alle details staan aan en je schildert vrij over de plaat. Werk met licht en donker, of daag jezelf uit met maar drie kleuren.",
colorReset:"Kleurplaat leegmaken", colorDone:"Ik ben klaar", colorFinished:"Kleurplaat klaar!", colorReset:"Kleurplaat leegmaken", colorDone:"Ik ben klaar", colorFinished:"Kleurplaat klaar!",
bgBlank:"Leeg blad", bgLines:"Lijntjes", bgWrite:"Schrijflijnen", bgBlank:"Leeg blad", bgLines:"Lijntjes", bgWrite:"Schrijflijnen",
bgGridS:"Kleine rekenblokjes", bgGridB:"Grote rekenblokjes", bgGridS:"Kleine rekenblokjes", bgGridB:"Grote rekenblokjes",
@ -483,7 +486,10 @@ const I18N = {
colorTheme:"Theme", colorPage:"Colouring page", colorProfile:"Year {year} · level {level}", colorTheme:"Theme", colorPage:"Colouring page", colorProfile:"Year {year} · level {level}",
colorThemeAll:"All themes", colorThemeAnimals:"Animals", colorThemeSeasons:"Seasons", colorThemeNature:"Nature", colorThemeScience:"Science & technology", colorThemeCulture:"Culture & surroundings", colorThemeAll:"All themes", colorThemeAnimals:"Animals", colorThemeSeasons:"Seasons", colorThemeNature:"Nature", colorThemeScience:"Science & technology", colorThemeCulture:"Culture & surroundings",
colorToolBrush:"Brush", colorToolBucket:"Paint bucket", colorToolBrush:"Brush", colorToolBucket:"Paint bucket",
colorBrushHint:"Paint smooth strokes with the brush; the outlines always stay visible.", colorBucketHint:"Tap an enclosed area to colour it in one go.", colorEraserHint:"Erase brush strokes, or tap and drag across a filled area to remove its paint-bucket colour too.", colorBrushHint:"Paint smooth strokes with the brush; the outlines always stay visible.", colorBucketHint:"Tap an enclosed area to colour it in one go; the bucket covers any brushwork in that area.", colorEraserHint:"The eraser lifts the top layer: first an area's fill, then the brush strokes underneath.",
colorGuide:"Colour guide", colorLevelHelp1:"Big areas, and your brush stays neatly inside the lines. The dots show which colour fits.",
colorLevelHelp2:"More areas to colour in. Switch on the colour guide if you want a hint.",
colorLevelHelp3:"Every detail is on and you paint freely across the page. Work with light and dark, or challenge yourself with just three colours.",
colorReset:"Clear colouring page", colorDone:"I'm finished", colorFinished:"Colouring page finished!", colorReset:"Clear colouring page", colorDone:"I'm finished", colorFinished:"Colouring page finished!",
bgBlank:"Blank sheet", bgLines:"Lines", bgWrite:"Handwriting lines", bgBlank:"Blank sheet", bgLines:"Lines", bgWrite:"Handwriting lines",
bgGridS:"Small maths squares", bgGridB:"Big maths squares", bgGridS:"Small maths squares", bgGridB:"Big maths squares",

View file

@ -1,218 +1,275 @@
/* teach — nieuwe kleurboekatlas voor groep 7-8 */ /* teach kleurplaten voor groep 7-8
De rijkste platen: veel vakken, fijnere vormen en composities waarin voor- en
achtergrond echt meedoen. Alles van achter naar voren getekend. */
"use strict"; "use strict";
function proFourSeasons(){return coloringScene( function plateSeasons(){
coloringPart("rect","seasons-paper",'x="4" y="4" width="792" height="592" rx="28"')+ /* één boom, vier seizoenen: de kroon is in vier kwarten verdeeld en de grond
coloringPart("path","seasons-ground",'d="M 4 466 C 144 427 278 447 401 478 C 535 511 663 479 796 441 L 796 596 L 4 596 Z"')+ eronder loopt mee, van links (lente) naar rechts (winter) */
coloringFigure("four-season-oak", /* de kroon is een cirkel in vier kwarten: linksonder lente, linksboven zomer,
coloringPart("path","seasons-trunk",'d="M 312 535 C 338 473 350 407 348 340 C 347 285 332 234 304 190 C 342 209 369 240 384 280 C 390 222 382 177 356 140 C 397 159 426 195 438 241 C 463 200 499 171 548 160 C 506 200 481 247 474 299 C 502 269 534 252 570 251 C 526 294 499 343 495 401 C 492 456 509 500 531 535 C 463 519 381 520 312 535 Z"')+ rechtsboven herfst, rechtsonder winter. De grond eronder loopt in vier
coloringPart("path","spring-canopy",'d="M 79 246 C 57 205 80 162 119 153 C 120 111 158 85 197 99 C 219 63 269 60 294 94 C 333 78 374 104 378 145 C 411 168 398 212 365 228 C 350 263 306 276 273 254 C 241 282 190 274 171 237 C 137 257 98 254 79 246 Z"')+ banen mee, dus de plaat leest van links naar rechts als een jaar. */
coloringPart("path","summer-canopy",'d="M 350 140 C 360 95 402 73 441 91 C 462 49 517 47 541 89 C 584 68 631 98 633 144 C 668 166 659 212 623 229 C 602 266 551 271 521 238 C 482 265 428 246 419 203 C 382 207 352 180 350 140 Z"',2)+ return coloringScene("four-seasons",
coloringPart("path","autumn-canopy",'d="M 83 272 C 55 294 64 337 96 351 C 88 393 125 422 163 405 C 188 442 242 439 265 399 C 306 414 342 382 332 342 C 361 312 340 269 301 263 C 280 229 231 227 207 258 C 171 232 116 243 83 272 Z"',2)+ coloringBackdrop("sky","#3b7dd8")+
coloringPart("path","winter-branches",'d="M 415 406 C 429 350 430 304 418 257 C 446 287 465 320 471 357 C 481 310 501 273 535 244 C 519 294 515 345 526 397 C 548 354 575 326 610 310 C 581 351 564 394 562 442 C 520 430 463 423 415 406 Z"',3)+ coloringFlat("ground-spring","M 4 452 L 200 452 L 200 596 L 4 596 Z","#3fae6a")+
coloringLine("path",'d="M 349 340 C 335 285 321 238 304 190 M 384 280 C 373 339 375 398 385 455 M 438 241 C 424 305 424 372 436 438 M 474 299 C 453 340 443 386 444 441 M 495 401 C 476 430 468 462 470 496 M 384 280 C 355 243 332 215 304 190 M 438 241 C 470 209 508 177 548 160" fill="none"',2) coloringFlat("ground-summer","M 200 452 L 400 452 L 400 596 L 200 596 Z","#f7c948")+
)+ coloringFlat("ground-autumn","M 400 452 L 600 452 L 600 596 L 400 596 Z","#f28c3c")+
coloringFigure("season-details", coloringFlat("ground-winter","M 600 452 L 796 452 L 796 596 L 600 596 Z","#ffffff")+
coloringFlower(183,174,"spring-blossom-a",.48,2)+ coloringGroup("tree",
coloringFlower(278,205,"spring-blossom-b",.42,3)+ coloringContour("trunk","M 344 452 C 356 400 364 340 366 250 L 434 250 C 436 340 444 400 456 452 Z","#8b5a3c")+
coloringPart("circle","summer-apple-a",'cx="488" cy="141" r="18"',2)+ coloringContour("crown-summer","M 400 210 L 250 210 A 150 150 0 0 1 400 60 Z","#3fae6a")+
coloringPart("circle","summer-apple-b",'cx="561" cy="183" r="17"',3)+ coloringContour("crown-autumn","M 400 210 L 400 60 A 150 150 0 0 1 550 210 Z","#f28c3c")+
coloringLeaf("autumn-leaf-a",153,319,-22,.62,2)+ coloringContour("crown-spring","M 400 210 L 400 360 A 150 150 0 0 1 250 210 Z","#d85aa5")+
coloringLeaf("autumn-leaf-b",256,362,34,.6,3)+ coloringContour("crown-winter","M 400 210 L 550 210 A 150 150 0 0 1 400 360 Z","#2fb7a8")+
coloringLine("path",'d="M 488 124 q 3 -20 18 -27 M 561 166 q 3 -19 17 -24 M 151 319 h 14 M 252 362 h 14 M 522 284 V 354 M 489 302 L 555 338 M 489 338 L 555 302" fill="none"',3) coloringGroup("tree-detail",
)+ coloringFlat("blossom-a","M 296 260 C 310 244 334 244 348 260 C 334 278 310 278 296 260 Z","#ffffff")+
coloringPart("path","season-path",'d="M 337 535 H 501 L 578 596 H 259 Z"',3) coloringFlat("blossom-b","M 320 306 C 334 290 358 290 372 306 C 358 324 334 324 320 306 Z","#ffffff")+
)} coloringFlat("leaf-a","M 300 152 C 314 136 338 136 352 152 C 338 170 314 170 300 152 Z","#f7c948")+
coloringFlat("leaf-b","M 452 152 C 466 136 490 136 504 152 C 490 170 466 170 452 152 Z","#e0554d")+
function proEcoCity(){return coloringScene( coloringFlat("snow-clump","M 452 262 C 466 246 490 246 504 262 C 490 280 466 280 452 262 Z","#ffffff")+
coloringPart("rect","eco-paper",'x="4" y="4" width="792" height="592" rx="28"')+ coloringFlat("trunk-knot","M 384 372 C 396 362 412 362 420 372 C 412 386 396 386 384 372 Z","#f2b38f")+
coloringPart("path","eco-park",'d="M 4 438 C 145 401 278 421 400 453 C 535 487 662 454 796 416 L 796 596 L 4 596 Z"')+ coloringFlat("hollow","M 386 418 C 396 406 410 406 418 418 C 410 434 396 434 386 418 Z","#26344a"),2)+
coloringFigure("passive-buildings", coloringLine("M 372 300 C 392 292 412 292 430 300 M 376 392 C 394 384 410 384 426 392",2)+
coloringPart("path","eco-building-left",'d="M 43 236 H 236 V 500 H 43 Z"')+ coloringLine("M 400 60 L 400 360 M 250 210 L 550 210 M 296 106 L 504 314 M 504 106 L 296 314",3))+
coloringPart("path","eco-building-center",'d="M 268 291 L 397 189 L 526 291 V 500 H 268 Z"',2)+ coloringGroup("season-props",
coloringPart("path","eco-building-right",'d="M 557 198 H 733 V 500 H 557 Z"',2)+ coloringContour("tulip-cup","M 78 448 C 78 424 90 408 108 408 C 126 408 138 424 138 448 C 122 458 94 458 78 448 Z","#e0554d")+
coloringPart("path","solar-array-left",'d="M 26 233 L 69 139 H 226 L 249 233 Z"')+ coloringFlat("tulip-stem","M 100 526 C 96 500 96 474 100 448 L 116 448 C 112 474 112 500 116 526 Z","#3fae6a")+
coloringPart("path","solar-array-right",'d="M 538 195 L 574 105 H 719 L 747 195 Z"',2)+ coloringFlat("sunflower-petal-a",coloringPetalPath(268,486,-90,48,20),"#f7c948")+
coloringPart("path","eco-door-left",'d="M 105 409 C 105 382 121 367 141 367 C 161 367 177 382 177 409 V 500 H 105 Z"',2)+ coloringFlat("sunflower-petal-b",coloringPetalPath(268,486,-18,48,20),"#f7c948")+
coloringPart("path","eco-door-center",'d="M 361 406 C 361 374 377 357 397 357 C 417 357 433 374 433 406 V 500 H 361 Z"',2)+ coloringFlat("sunflower-petal-c",coloringPetalPath(268,486,54,48,20),"#f7c948")+
coloringPart("path","eco-door-right",'d="M 611 409 C 611 382 627 367 647 367 C 667 367 683 382 683 409 V 500 H 611 Z"',3)+ coloringFlat("sunflower-petal-d",coloringPetalPath(268,486,126,48,20),"#f7c948")+
coloringLine("path",'d="M 51 188 H 238 M 91 139 L 72 233 M 139 139 L 126 233 M 187 139 L 181 233 M 552 151 H 735 M 608 105 L 589 195 M 657 105 L 646 195 M 704 105 L 698 195 M 78 278 H 123 V 331 H 78 Z M 158 278 H 203 V 331 H 158 Z M 78 340 H 123 V 386 H 78 Z M 158 340 H 203 V 386 H 158 Z M 304 308 H 350 V 358 H 304 Z M 444 308 H 490 V 358 H 444 Z M 585 245 H 630 V 300 H 585 Z M 663 245 H 708 V 300 H 663 Z M 585 315 H 630 V 365 H 585 Z M 663 315 H 708 V 365 H 663 Z" fill="none"',3) coloringFlat("sunflower-petal-e",coloringPetalPath(268,486,198,48,20),"#f7c948")+
)+ coloringCircle("sunflower-core",268,486,26,"#8b5a3c","coloring-contour")+
coloringFigure("vertical-wind-turbine", coloringFlat("pumpkin","M 484 552 C 484 518 508 498 540 498 C 572 498 596 518 596 552 C 596 578 572 590 540 590 C 508 590 484 578 484 552 Z","#f28c3c")+
coloringPart("path","turbine-mast",'d="M 734 196 H 751 L 766 500 H 716 Z"',2)+ coloringGroup("season-props-detail",
coloringPart("path","turbine-blade-one",'d="M 741 193 C 702 157 684 117 692 82 C 727 97 744 141 751 187 Z"',2)+ coloringFlat("pumpkin-stem","M 532 498 C 532 484 536 474 546 468 C 552 480 550 492 546 500 Z","#3fae6a")+
coloringPart("path","turbine-blade-two",'d="M 751 190 C 790 171 825 174 844 197 C 818 218 783 214 751 201 Z"',3)+ coloringLine("M 512 506 C 506 528 506 566 512 584 M 568 506 C 574 528 574 566 568 584",2)+
coloringPart("path","turbine-blade-three",'d="M 746 203 C 729 246 704 275 674 282 C 671 246 700 217 739 196 Z"',3)+ coloringFlat("snowman-body","M 668 552 C 668 526 686 508 710 508 C 734 508 752 526 752 552 C 752 576 734 588 710 588 C 686 588 668 576 668 552 Z","#ffffff")+
coloringPart("circle","turbine-hub",'cx="747" cy="197" r="18"',2) coloringCircle("snowman-head",710,478,32,"#ffffff")+
,'transform="translate(-55 0)"')+ coloringDot(700,472,5)+coloringDot(720,472,5),2)+
coloringFigure("cargo-bicycle", coloringLine("M 108 526 C 128 508 148 500 166 498 M 268 512 C 264 542 264 566 268 586",3))
coloringPart("circle","eco-bike-wheel-a",'cx="226" cy="537" r="38"',3)+ );
coloringPart("circle","eco-bike-wheel-b",'cx="355" cy="537" r="38"',3)+
coloringPart("path","eco-bike-box",'d="M 109 476 H 215 L 226 536 H 121 Z"',3)+
coloringLine("path",'d="M 226 537 L 278 468 L 317 537 H 226 L 278 537 L 302 482 H 344 M 278 468 H 252 M 317 537 H 355 M 338 475 l 20 -20 M 296 482 h 32" fill="none"',2)
)+
coloringFigure("eco-street-trees",
coloringPart("path","eco-tree-left-trunk",'d="M 22 417 H 49 V 514 H 22 Z"',3)+
coloringPart("path","eco-tree-left-crown",'d="M 4 394 C 5 359 32 338 63 346 C 83 329 113 344 120 371 C 121 402 95 419 68 413 C 43 430 11 420 4 394 Z"',2)+
coloringPart("path","eco-tree-right-trunk",'d="M 524 421 H 551 V 510 H 524 Z"',3)+
coloringPart("path","eco-tree-right-crown",'d="M 486 401 C 484 366 512 343 543 351 C 566 333 598 351 601 382 C 601 411 573 425 548 416 C 523 434 492 425 486 401 Z"',3)
)
)}
function proMandala(){
const outer=[0,30,60,90,120,150,180,210,240,270,300,330].map((angle,index)=>
coloringPart("path","mandala-outer-"+index,'d="M 400 61 C 356 112 354 166 400 204 C 446 166 444 112 400 61 Z" transform="rotate('+angle+' 400 300)"',index%3===0?2:3)
).join("");
const middle=[0,45,90,135,180,225,270,315].map((angle,index)=>
coloringPart("path","mandala-middle-"+index,'d="M 400 170 C 363 210 365 252 400 281 C 435 252 437 210 400 170 Z" transform="rotate('+angle+' 400 300)"',index%2?3:2)
).join("");
const inner=[0,60,120,180,240,300].map((angle,index)=>
coloringPart("path","mandala-inner-"+index,'d="M 400 238 C 374 267 375 299 400 320 C 425 299 426 267 400 238 Z" transform="rotate('+angle+' 400 300)"',2)
).join("");
return coloringScene(
coloringPart("rect","mandala-paper",'x="4" y="4" width="792" height="592" rx="28"')+
coloringFigure("botanical-rosette",
outer+middle+inner+
coloringPart("circle","mandala-center",'cx="400" cy="300" r="55"')+
coloringPart("path","mandala-center-star",'d="M 400 258 L 412 286 L 442 300 L 412 314 L 400 342 L 388 314 L 358 300 L 388 286 Z"',2)+
coloringLine("circle",'cx="400" cy="300" r="239" fill="none"',3)+
coloringLine("circle",'cx="400" cy="300" r="144" fill="none"',2)+
coloringLine("path",'d="M 400 46 V 61 M 400 539 V 554 M 146 300 H 161 M 639 300 H 654 M 220 120 L 232 132 M 568 468 L 580 480 M 220 480 L 232 468 M 568 132 L 580 120" fill="none"',3)
)
)
} }
function proEcosystem(){return coloringScene( function plateEcoCity(){
coloringPart("rect","pond-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("eco-city",
coloringPart("path","pond-bank",'d="M 4 398 C 145 359 276 380 400 412 C 535 446 663 414 796 376 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringPart("path","pond-water",'d="M 50 476 C 107 405 239 390 402 406 C 563 422 696 416 752 486 C 696 552 564 576 401 571 C 236 568 103 548 50 476 Z"',2)+ coloringCircle("sun",112,104,48,"#f7c948","coloring-contour")+
coloringFigure("common-frog", coloringFlat("cloud",coloringCloudPath(520,52,180,54),"#ffffff")+
coloringPart("path","frog-far-hind-leg",'d="M 370 416 C 310 389 249 403 198 449 C 231 476 278 484 329 467 L 397 442 Z"',2)+ coloringFlat("far-hills","M 4 328 C 120 282 220 322 320 296 C 410 272 480 308 560 292 C 650 274 720 306 796 292 L 796 372 L 4 372 Z","#2fb7a8")+
coloringPart("path","frog-far-foot",'d="M 205 447 C 164 455 123 481 102 514 C 137 524 173 515 204 491 C 221 514 244 528 270 526 C 258 493 240 467 205 447 Z"',3)+ coloringFlat("street","M 4 492 L 796 492 L 796 596 L 4 596 Z","#26344a")+
coloringPart("path","frog-near-hind-leg",'d="M 383 434 C 334 435 291 462 270 505 C 305 523 351 514 389 486 C 419 465 443 451 466 445 Z"')+ coloringFlat("park","M 4 372 L 796 372 L 796 492 L 4 492 Z","#3fae6a")+
coloringPart("path","frog-near-foot",'d="M 277 499 C 238 510 204 536 192 565 C 224 570 255 558 281 535 C 301 554 323 563 348 558 C 334 529 311 508 277 499 Z"',2)+ coloringGroup("turbines",
coloringPart("path","frog-silhouette",'d="M 286 405 C 298 344 354 310 422 318 C 464 323 498 342 520 365 C 543 343 579 337 609 354 C 642 373 654 413 635 444 C 617 472 579 482 546 467 C 515 493 468 506 415 498 C 337 488 276 458 286 405 Z"')+ coloringFlat("turbine-mast","M 128 372 L 148 372 L 142 176 L 134 176 Z","#ffffff")+
coloringPart("path","frog-head-profile",'d="M 502 365 C 531 335 580 334 611 357 C 640 379 649 416 631 443 C 617 465 589 474 561 467 C 536 456 519 440 507 418 Z"',2)+ coloringFlat("turbine-blade-a","M 138 176 L 146 168 L 236 108 L 240 124 Z","#ffffff")+
coloringPart("path","frog-visible-eye",'d="M 548 357 C 548 329 568 310 592 315 C 617 320 626 349 611 370 C 593 383 566 376 548 357 Z"',2)+ coloringFlat("turbine-blade-b","M 138 176 L 130 170 L 60 244 L 74 252 Z","#ffffff")+
coloringPart("circle","frog-tympanum",'cx="550" cy="405" r="24"',3)+ coloringFlat("turbine-blade-c","M 138 176 L 146 182 L 168 284 L 152 286 Z","#ffffff")+
coloringPart("path","frog-side-fold",'d="M 363 348 C 405 329 462 339 498 371 C 486 407 489 451 509 480 C 466 502 407 500 365 474 C 337 440 336 386 363 348 Z"',3)+ coloringCircle("turbine-hub",138,176,13,"#26344a","coloring-contour"))+
coloringDot(592,341,5)+ coloringGroup("tower-block",
coloringLine("path",'d="M 602 403 C 615 408 626 407 635 401 M 368 474 C 337 487 309 505 286 531 M 478 474 C 511 481 545 478 571 466" fill="none"',2) coloringContour("tower","M 296 492 L 296 196 L 412 196 L 412 492 Z","#f2b38f")+
)+ coloringFlat("tower-roof","M 288 196 L 420 196 L 420 176 L 288 176 Z","#e0554d")+
coloringFigure("pond-life", coloringGroup("tower-windows",
coloringPart("path","lily-pad-left",'d="M 67 503 C 111 463 193 462 235 504 L 184 515 C 204 535 201 558 185 570 C 130 572 79 549 67 503 Z"',2)+ coloringFlat("tower-window-a","M 316 224 L 350 224 L 350 260 L 316 260 Z","#f7c948")+
coloringPart("path","lily-pad-right",'d="M 554 515 C 599 478 683 482 720 527 L 669 538 C 687 558 681 577 665 587 C 612 589 567 564 554 515 Z"',3)+ coloringFlat("tower-window-b","M 360 224 L 394 224 L 394 260 L 360 260 Z","#f7c948")+
coloringFlower(629,493,"pond-water-lily",.62,3)+ coloringFlat("tower-window-c","M 316 284 L 350 284 L 350 320 L 316 320 Z","#f7c948")+
coloringPart("path","pond-minnow",'d="M 314 536 C 350 506 411 506 450 538 C 411 566 350 566 314 536 Z"',3)+ coloringFlat("tower-window-d","M 360 284 L 394 284 L 394 320 L 360 320 Z","#f7c948")+
coloringPart("path","pond-minnow-tail",'d="M 314 536 L 276 511 L 283 560 Z"',3)+ coloringFlat("tower-window-e","M 316 344 L 350 344 L 350 380 L 316 380 Z","#f7c948")+
coloringDot(423,535,4,3) coloringFlat("tower-window-f","M 360 344 L 394 344 L 394 380 L 360 380 Z","#f7c948")+
)+ coloringFlat("tower-door","M 336 492 L 376 492 L 376 428 C 368 416 344 416 336 428 Z","#8b5a3c"),2)+
coloringFigure("pond-reeds", coloringLine("M 296 404 L 412 404",3))+
coloringPart("path","pond-reed-left",'d="M 78 432 C 63 361 75 288 103 230 C 121 294 114 365 101 437 Z"',2)+ coloringGroup("solar-house",
coloringPart("path","pond-reed-right",'d="M 703 430 C 690 357 706 287 735 237 C 751 302 744 369 730 437 Z"',3)+ coloringContour("house","M 468 492 L 468 336 L 604 336 L 604 492 Z","#f2b38f")+
coloringLine("path",'d="M 103 230 C 75 208 77 170 104 155 C 130 174 129 212 103 230 M 735 237 C 710 211 714 176 739 161 C 762 181 759 216 735 237" fill="none"',3) coloringFlat("roof","M 452 336 L 536 268 L 620 336 Z","#e0554d")+
) coloringFlat("solar-panel","M 546 280 L 610 332 L 566 332 L 512 288 Z","#3b7dd8")+
)} coloringGroup("house-detail",
coloringFlat("house-window-left","M 492 368 L 528 368 L 528 404 L 492 404 Z","#f7c948")+
coloringFlat("house-window-right","M 548 368 L 584 368 L 584 404 L 548 404 Z","#f7c948")+
coloringFlat("house-door","M 516 492 L 556 492 L 556 428 C 548 418 524 418 516 428 Z","#8b5a3c")+
coloringLine("M 520 288 L 578 332 M 528 282 L 592 332",2))+
coloringLine("M 468 440 L 604 440",3))+
coloringGroup("small-house",
coloringContour("shop","M 644 492 L 644 384 L 756 384 L 756 492 Z","#f7c948")+
coloringFlat("shop-roof","M 632 384 L 700 330 L 768 384 Z","#8b5a3c")+
coloringGroup("shop-detail",
coloringFlat("shop-window","M 664 412 L 728 412 L 728 456 L 664 456 Z","#2fb7a8")+
coloringFlat("shop-awning","M 656 412 L 736 412 L 744 392 L 648 392 Z","#e0554d"),2))+
coloringGroup("tram",
coloringContour("tram-body","M 104 492 L 104 404 C 104 394 112 388 124 388 L 268 388 C 280 388 288 394 288 404 L 288 492 Z","#e0554d")+
coloringGroup("tram-detail",
coloringFlat("tram-window-a","M 122 408 L 168 408 L 168 444 L 122 444 Z","#2fb7a8")+
coloringFlat("tram-window-b","M 180 408 L 226 408 L 226 444 L 180 444 Z","#2fb7a8")+
coloringFlat("tram-window-c","M 238 408 L 276 408 L 276 444 L 238 444 Z","#2fb7a8")+
coloringCircle("tram-wheel-left",148,500,20,"#26344a")+
coloringCircle("tram-wheel-right",244,500,20,"#26344a"),2)+
coloringLine("M 104 464 L 288 464 M 196 388 L 196 336",3))+
coloringGroup("trees",
coloringFlat("city-tree-trunk-a","M 424 492 L 424 452 L 444 452 L 444 492 Z","#8b5a3c")+
coloringCircle("city-tree-crown-a",434,414,42,"#3fae6a","coloring-contour")+
coloringGroup("city-tree-b",
coloringFlat("city-tree-trunk-b","M 620 492 L 620 456 L 636 456 L 636 492 Z","#8b5a3c")+
coloringCircle("city-tree-crown-b",628,426,34,"#3fae6a","coloring-contour"),2))+
coloringGroup("street-detail",
coloringLine("M 40 544 L 128 544 M 176 544 L 264 544 M 312 544 L 400 544 M 448 544 L 536 544 M 584 544 L 672 544 M 720 544 L 780 544",2),2)
);
}
function proSolarObservatory(){return coloringScene( function plateMandala(){
coloringPart("rect","observatory-paper",'x="4" y="4" width="792" height="592" rx="28"')+ const centreX=400,centreY=300;
coloringFigure("observatory-mountains", const ring=(prefix,count,inner,outer,wide,hint,cls)=>{
coloringPart("path","observatory-mountain-left",'d="M 4 431 L 168 208 L 332 431 Z"')+ let out="";
coloringPart("path","observatory-mountain-right",'d="M 473 431 L 644 215 L 796 431 Z"',2)+ for(let index=0;index<count;index++){
coloringPart("path","observatory-snow-left",'d="M 124 267 L 168 208 L 215 270 L 193 257 L 169 283 L 147 256 Z"',2)+ const angle=-90+index*360/count,rad=angle*Math.PI/180;
coloringPart("path","observatory-snow-right",'d="M 602 269 L 644 215 L 686 273 L 666 261 L 645 285 L 623 260 Z"',3) const x=centreX+Math.cos(rad)*inner,y=centreY+Math.sin(rad)*inner;
)+ out+=coloringRegion("path",`${prefix}-${index}`,`d="${coloringPetalPath(x,y,angle,outer-inner,wide)}"`,hint,cls);
coloringPart("path","observatory-hill",'d="M 4 431 C 145 395 277 416 400 448 C 535 482 662 451 796 412 L 796 596 L 4 596 Z"')+ }
coloringFigure("astronomical-observatory", return out;
coloringPart("path","observatory-base",'d="M 169 383 H 585 C 615 383 635 403 635 433 V 544 H 169 Z"')+ };
coloringPart("path","observatory-dome",'d="M 194 383 C 207 269 285 212 391 213 C 499 214 574 271 587 383 Z"')+ const studs=(prefix,count,radius,size,hint)=>{
coloringPart("path","observatory-dome-slit",'d="M 365 215 C 382 211 400 211 417 216 L 427 383 H 350 Z"',2)+ let out="";
coloringPart("path","observatory-door",'d="M 520 449 C 520 420 535 404 555 404 C 575 404 590 420 590 449 V 544 H 520 Z"',2)+ for(let index=0;index<count;index++){
coloringPart("path","observatory-control-panel",'d="M 210 441 H 326 V 508 H 210 Z"',3)+ const angle=(-90+180/count+index*360/count)*Math.PI/180;
coloringLine("path",'d="M 181 383 H 620 M 226 463 H 311 M 226 483 H 311 M 226 503 H 311" fill="none"',3) out+=coloringCircle(`${prefix}-${index}`,coloringRound(centreX+Math.cos(angle)*radius),coloringRound(centreY+Math.sin(angle)*radius),size,hint);
)+ }
coloringFigure("research-telescope", return out;
coloringPart("path","telescope-mount",'d="M 351 370 L 390 286 L 431 370 Z"',2)+ };
coloringPart("path","telescope-barrel",'d="M 387 290 L 567 168 L 604 212 L 423 333 Z"')+ const spokes=count=>{
coloringPart("path","telescope-lens",'d="M 562 164 C 583 142 619 161 626 187 C 624 216 598 229 575 215 C 559 203 554 181 562 164 Z"',2)+ let out="";
coloringPart("path","telescope-brace",'d="M 385 323 L 354 455 H 382 L 416 329 Z"',3)+ for(let index=0;index<count;index++){
coloringPart("circle","telescope-pivot",'cx="407" cy="309" r="23"',2)+ const rad=(-90+index*360/count)*Math.PI/180;
coloringLine("path",'d="M 423 306 L 583 199 M 390 286 L 407 309" fill="none"',3) out+=`M ${coloringRound(centreX+Math.cos(rad)*54)} ${coloringRound(centreY+Math.sin(rad)*54)} L ${coloringRound(centreX+Math.cos(rad)*272)} ${coloringRound(centreY+Math.sin(rad)*272)} `;
)+ }
coloringStars([[82,91,12],[289,101,9],[675,84,14],[735,145,8]],"observatory-star",3)+ return out;
coloringPart("circle","observatory-moon",'cx="111" cy="156" r="41"',3) };
)} return coloringScene("mandala",
coloringBackdrop("mandala-paper","#f2b38f")+
coloringGroup("ring-outer",ring("petal-outer",16,182,274,30,"#7557c8","coloring-contour"))+
coloringGroup("ring-mid",ring("petal-mid",12,112,182,30,"#3b7dd8","coloring-contour"))+
coloringGroup("ring-inner",ring("petal-inner",8,46,112,26,"#2fb7a8","coloring-flat"),2)+
coloringGroup("ring-studs",studs("stud-outer",16,196,15,"#f7c948")+studs("stud-mid",12,126,13,"#e0554d"),2)+
coloringCircle("mandala-core",centreX,centreY,46,"#f7c948","coloring-contour")+
coloringGroup("mandala-heart",
coloringCircle("mandala-heart-ring",centreX,centreY,28,"#d85aa5")+
coloringCircle("mandala-heart-dot",centreX,centreY,13,"#e0554d"),2)+
coloringGroup("mandala-lines",coloringLine(spokes(24),3),3)
);
}
function proSavannaWeb(){return coloringScene( function plateEcosystem(){
coloringPart("rect","savanna-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("ecosystem",
coloringPart("path","savanna-ground",'d="M 4 426 C 145 389 277 410 400 442 C 535 476 662 444 796 405 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringFigure("savanna-acacia", coloringFlat("far-bank","M 4 300 C 130 268 250 296 370 280 C 480 266 580 292 690 278 C 740 272 776 280 796 292 L 796 356 L 4 356 Z","#3fae6a")+
coloringPart("path","acacia-trunk",'d="M 51 454 C 77 386 87 317 82 249 C 79 202 65 162 44 130 C 80 146 105 174 119 209 C 141 170 173 145 214 137 C 183 177 168 222 171 271 C 176 342 195 405 224 454 Z"',2)+ coloringFlat("pond","M 4 356 L 796 356 L 796 500 L 4 500 Z","#2fb7a8")+
coloringPart("path","acacia-crown",'d="M 9 156 C 13 113 50 89 90 101 C 111 62 161 56 188 91 C 224 68 274 88 284 129 C 324 137 342 179 320 211 C 297 235 258 236 232 216 C 198 243 148 235 129 200 C 96 224 49 214 34 180 C 21 176 12 167 9 156 Z"',2) coloringFlat("near-bank","M 4 500 C 160 484 340 494 490 506 C 610 516 700 512 796 498 L 796 596 L 4 596 Z","#8b5a3c")+
)+ coloringGroup("reeds",
coloringFigure("african-lion", coloringFlat("reed-left-a","M 68 500 C 62 440 66 386 78 340 L 96 344 C 86 388 84 442 90 500 Z","#3fae6a")+
coloringPart("path","lion-tail",'d="M 367 350 C 405 331 438 344 449 374 C 459 402 445 426 423 435 C 441 406 428 382 404 384 C 389 385 382 395 379 409 L 351 394 Z"',2)+ coloringFlat("reed-left-b","M 112 500 C 108 452 114 408 126 372 L 142 378 C 132 410 128 454 132 500 Z","#3fae6a")+
[211,263,322,361].map((x,i)=>coloringPart("path","lion-leg-"+i,`d="M ${x} 411 C ${x-3} 452 ${x-4} 500 ${x+3} 535 C ${x+17} 541 ${x+37} 540 ${x+50} 533 L ${x+45} 517 C ${x+37} 480 ${x+40} 443 ${x+34} 405 Z"`,i>1?2:1)).join("")+ coloringFlat("reed-head-a","M 78 340 C 70 314 76 292 92 284 C 104 296 106 320 96 344 Z","#8b5a3c")+
coloringPart("path","lion-body",'d="M 154 371 C 160 310 217 277 296 282 C 370 287 419 324 418 378 C 417 433 364 463 291 459 C 216 456 148 427 154 371 Z"')+ coloringGroup("reeds-extra",
coloringPart("path","lion-mane",'d="M 105 245 C 120 217 151 207 176 221 C 197 199 232 207 244 235 C 273 237 288 265 279 292 C 301 314 292 348 267 361 C 269 392 243 414 214 407 C 194 430 160 425 145 399 C 116 406 92 382 96 353 C 72 336 71 302 94 284 C 87 266 92 252 105 245 Z"')+ coloringFlat("reed-right-a","M 712 500 C 706 444 710 394 722 350 L 740 354 C 730 396 728 446 734 500 Z","#3fae6a")+
coloringPart("path","lion-face",'d="M 199 264 C 178 241 141 241 119 261 C 105 274 101 290 105 308 L 73 318 C 78 339 99 350 123 347 C 135 369 164 378 190 365 C 219 350 232 316 220 287 C 216 277 208 269 199 264 Z"',2)+ coloringFlat("reed-right-b","M 756 500 C 752 456 758 414 770 380 L 786 386 C 776 416 772 458 776 500 Z","#3fae6a")+
coloringPart("path","lion-muzzle",'d="M 105 307 L 73 318 C 78 339 99 350 123 347 C 136 338 137 322 127 311 Z"',3)+ coloringFlat("reed-head-b","M 722 350 C 714 324 720 302 736 294 C 748 306 750 330 740 354 Z","#8b5a3c"),2))+
coloringPart("path","lion-ear-left",'d="M 119 257 C 103 231 110 208 132 201 C 150 220 149 240 137 261 Z"',3)+ coloringGroup("lilies",
coloringPart("path","lion-ear-right",'d="M 185 250 C 197 222 220 212 239 226 C 231 250 213 261 190 266 Z"',3)+ coloringFlat("lily-pad-a","M 200 452 C 200 428 236 412 276 412 C 316 412 348 428 348 452 C 348 474 316 486 274 486 C 234 486 200 474 200 452 Z","#3fae6a")+
coloringDot(151,286,5)+coloringDot(82,325,5)+ coloringFlat("lily-pad-b","M 520 470 C 520 450 550 436 584 436 C 618 436 646 450 646 470 C 646 488 618 500 582 500 C 548 500 520 488 520 470 Z","#3fae6a")+
coloringLine("path",'d="M 119 328 C 105 334 94 333 84 328 M 122 347 C 140 356 159 356 176 349 M 224 535 H 256 M 276 535 H 308 M 335 535 H 367 M 374 535 H 406" fill="none"',2) coloringGroup("lily-flower",
)+ coloringFlat("lily-petal-a",coloringPetalPath(584,432,-90,42,17),"#d85aa5")+
coloringFigure("plains-zebra", coloringFlat("lily-petal-b",coloringPetalPath(584,432,-138,38,16),"#d85aa5")+
coloringPart("path","zebra-tail",'d="M 492 347 C 459 328 431 342 427 371 C 431 399 453 416 479 407 L 504 386 Z"',3)+ coloringFlat("lily-petal-c",coloringPetalPath(584,432,-42,38,16),"#d85aa5")+
[522,573,642,689].map((x,i)=>coloringPart("path","zebra-leg-"+i,`d="M ${x} 414 C ${x-3} 454 ${x-4} 501 ${x+2} 536 C ${x+14} 542 ${x+33} 542 ${x+45} 535 L ${x+41} 520 C ${x+33} 482 ${x+36} 447 ${x+31} 408 Z"`,3)).join("")+ coloringCircle("lily-heart",584,432,15,"#f7c948"),2)+
coloringPart("path","zebra-body",'d="M 476 371 C 481 316 532 287 602 291 C 671 295 714 328 715 378 C 713 428 666 457 598 454 C 526 452 471 424 476 371 Z"',2)+ coloringLine("M 274 418 L 274 452 M 226 444 L 348 452 M 582 442 L 582 470",3))+
coloringPart("path","zebra-neck",'d="M 659 333 C 662 288 679 246 707 214 L 756 238 C 736 285 728 333 725 385 Z"',2)+ coloringGroup("fish",
coloringPart("path","zebra-head",'d="M 696 210 C 724 178 767 174 797 202 C 821 225 815 266 787 288 C 756 311 711 300 692 268 C 679 246 682 225 696 210 Z"',2)+ coloringFlat("pond-fish-tail","M 466 396 C 448 380 434 374 422 376 C 426 392 426 408 422 424 C 436 424 452 414 466 400 Z","#f28c3c")+
coloringPart("path","zebra-muzzle",'d="M 758 251 C 781 235 815 243 826 267 C 808 290 773 295 754 274 Z"',3)+ coloringContour("pond-fish-body","M 462 400 C 474 372 500 356 530 356 C 562 356 586 374 596 400 C 586 426 562 442 530 442 C 500 442 474 426 462 400 Z","#f28c3c")+
coloringPart("path","zebra-ear-left",'d="M 708 203 L 699 160 L 733 192 Z"',3)+ coloringGroup("pond-fish-detail",
coloringPart("path","zebra-ear-right",'d="M 748 187 L 771 151 L 779 199 Z"',3)+ coloringCircle("pond-fish-eye",570,388,11,"#ffffff")+
coloringPart("path","zebra-mane",'d="M 684 236 C 662 274 658 316 667 359 L 688 344 C 681 302 690 265 714 224 Z"',3)+ coloringDot(571,388,5)+
coloringDot(769,217,4)+ coloringLine("M 500 380 C 514 376 528 374 542 374 M 500 418 C 514 422 528 424 542 424",2)))+
coloringLine("path",'d="M 508 306 C 529 348 530 409 517 443 M 551 292 C 573 341 573 416 558 450 M 600 291 C 618 336 619 412 606 454 M 648 300 C 664 339 665 405 654 450 M 681 265 L 727 293 M 675 298 L 724 325 M 675 332 L 724 358 M 775 272 C 787 277 798 275 807 268 M 535 536 H 568 M 585 536 H 619 M 655 536 H 687 M 702 536 H 735" fill="none"',3) coloringGroup("frog",
,'transform="translate(-30 0)"')+ coloringFlat("frog-far-leg","M 240 512 C 208 508 182 518 166 538 C 190 550 224 548 248 534 Z","#3fae6a")+
coloringPart("circle","savanna-sun",'cx="692" cy="83" r="42"',3) coloringFlat("frog-near-leg","M 372 514 C 404 510 432 520 448 540 C 424 552 390 550 366 536 Z","#3fae6a")+
)} coloringContour("frog-body","M 244 496 C 244 448 284 418 334 418 C 384 418 424 448 424 496 C 424 532 384 548 334 548 C 284 548 244 532 244 496 Z","#3fae6a")+
coloringContour("frog-head","M 262 434 C 262 398 294 376 334 376 C 374 376 406 398 406 434 C 406 458 374 470 334 470 C 294 470 262 458 262 434 Z","#3fae6a")+
coloringGroup("frog-detail",
coloringCircle("frog-eye-left",302,388,22,"#f7c948")+
coloringCircle("frog-eye-right",366,388,22,"#f7c948")+
coloringDot(302,388,9)+coloringDot(366,388,9)+
coloringFlat("frog-belly","M 266 496 C 282 526 302 540 334 542 C 366 540 388 526 402 496 C 400 532 372 548 334 548 C 296 548 268 532 266 496 Z","#f7c948"),2)+
coloringLine("M 306 448 C 320 458 348 458 362 448",2)+
coloringLine("M 274 480 C 296 490 372 490 394 480 M 260 524 C 284 536 384 536 408 524",3))+
coloringGroup("dragonfly",
coloringFlat("dragonfly-wing-upper","M 596 236 C 636 214 690 212 720 226 C 690 250 638 256 600 248 Z","#2fb7a8")+
coloringFlat("dragonfly-wing-lower","M 596 252 C 636 268 686 276 714 290 C 682 302 632 292 598 268 Z","#2fb7a8")+
coloringContour("dragonfly-body","M 566 232 C 578 226 592 226 600 234 C 602 262 598 296 588 322 C 578 300 570 266 566 232 Z","#7557c8")+
coloringCircle("dragonfly-head",582,220,20,"#7557c8","coloring-contour")+
coloringGroup("dragonfly-detail",
coloringDot(574,214,5)+coloringDot(590,214,5)+
coloringLine("M 572 254 L 596 254 M 574 274 L 594 274 M 578 294 L 592 294",2)))+
coloringGroup("front-reeds",
coloringLine("M 160 590 C 168 556 172 534 170 512 M 190 594 C 200 562 204 542 204 520 M 640 588 C 648 556 652 534 650 514",2),2)
);
}
function proJapaneseGarden(){return coloringScene( function plateSavanna(){
coloringPart("rect","japanese-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("savanna-web",
coloringPart("path","japanese-bank",'d="M 4 410 C 145 371 277 391 400 423 C 535 458 663 426 796 388 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#f2b38f")+
coloringPart("path","japanese-pond",'d="M 49 486 C 116 420 254 409 406 430 C 558 451 687 438 750 500 C 685 561 556 582 405 574 C 246 567 108 550 49 486 Z"',2)+ coloringCircle("sun",654,124,64,"#f28c3c","coloring-contour")+
coloringFigure("japanese-pagoda", coloringFlat("far-plain","M 4 316 C 140 288 280 306 420 320 C 550 332 680 326 796 306 L 796 396 L 4 396 Z","#f7c948")+
coloringPart("path","pagoda-base",'d="M 550 312 H 715 V 474 H 550 Z"')+ coloringFlat("plain","M 4 396 L 796 396 L 796 596 L 4 596 Z","#f7c948")+
coloringPart("path","pagoda-roof-low",'d="M 502 314 C 552 286 667 285 764 315 L 719 351 H 546 Z"')+ coloringFlat("waterhole","M 96 552 C 96 522 168 504 250 504 C 332 504 396 522 396 552 C 396 578 332 590 248 590 C 166 590 96 578 96 552 Z","#2fb7a8")+
coloringPart("path","pagoda-upper",'d="M 577 218 H 689 V 294 H 577 Z"',2)+ coloringGroup("acacia",
coloringPart("path","pagoda-roof-high",'d="M 536 220 C 580 187 654 185 732 221 L 690 252 H 575 Z"',2)+ coloringContour("acacia-trunk","M 116 396 C 132 344 146 288 150 224 L 200 224 C 204 288 218 344 234 396 Z","#8b5a3c")+
coloringPart("path","pagoda-top",'d="M 604 148 H 662 V 199 H 604 Z"',3)+ coloringFlat("acacia-branch-left","M 150 236 C 116 226 84 214 56 200 L 62 182 C 92 196 124 208 156 216 Z","#8b5a3c")+
coloringPart("path","pagoda-roof-top",'d="M 574 150 C 606 124 654 124 695 151 L 663 176 H 603 Z"',3)+ coloringFlat("acacia-branch-right","M 200 236 C 234 226 266 214 294 200 L 288 182 C 258 196 226 208 194 216 Z","#8b5a3c")+
coloringPart("path","pagoda-door",'d="M 610 390 C 610 365 621 352 633 352 C 646 352 657 365 657 390 V 474 H 610 Z"',2)+ coloringContour("acacia-canopy","M 30 196 C 12 168 40 138 84 138 C 106 112 168 106 202 130 C 254 122 304 146 306 180 C 308 198 288 208 254 206 L 80 206 C 50 206 34 202 30 196 Z","#3fae6a")+
coloringLine("path",'d="M 587 351 V 474 M 678 351 V 474 M 601 252 V 294 M 663 252 V 294 M 633 124 V 94 M 550 403 H 715" fill="none"',3) coloringGroup("acacia-detail",
)+ coloringFlat("acacia-cluster-a","M 74 172 C 94 154 128 154 146 172 C 126 192 94 192 74 172 Z","#2fb7a8")+
coloringFigure("arched-japanese-bridge", coloringFlat("acacia-cluster-b","M 186 164 C 206 146 240 146 258 164 C 238 184 206 184 186 164 Z","#2fb7a8")+
coloringPart("path","japanese-bridge-deck",'d="M 71 479 C 137 384 281 365 390 449 L 390 499 C 281 427 151 443 83 529 Z"')+ coloringLine("M 166 232 C 162 288 164 344 172 392 M 186 232 C 182 288 186 344 194 392",2))+
coloringPart("path","japanese-bridge-rail",'d="M 72 453 C 149 354 287 348 396 430 L 390 455 C 284 383 153 397 80 486 Z"',2)+ coloringLine("M 80 206 C 120 202 240 202 280 206 M 100 154 C 130 148 200 148 240 154",3))+
coloringPart("path","japanese-bridge-post-left",'d="M 83 402 H 106 V 513 H 83 Z"',2)+ coloringGroup("giraffe",
coloringPart("path","japanese-bridge-post-right",'d="M 353 386 H 376 V 492 H 353 Z"',3)+ coloringFlat("giraffe-far-leg","M 606 470 L 606 566 C 606 580 640 580 640 566 L 640 470 Z","#f7c948")+
coloringLine("path",'d="M 109 424 V 485 M 158 385 V 456 M 214 367 V 436 M 271 364 V 432 M 326 378 V 455" fill="none"',3) coloringFlat("giraffe-near-leg","M 542 476 L 542 572 C 542 588 580 588 580 572 L 580 476 Z","#f7c948")+
)+ coloringFlat("giraffe-tail","M 654 448 C 674 464 686 486 688 510 C 676 506 664 492 654 472 Z","#f7c948")+
coloringFigure("cherry-tree", coloringContour("giraffe-body","M 522 452 C 522 408 564 384 614 384 C 662 384 692 408 692 452 C 692 486 662 502 610 502 C 560 502 522 486 522 452 Z","#f7c948")+
coloringPart("path","cherry-trunk",'d="M 38 409 C 66 350 77 287 73 225 C 70 181 56 141 35 108 C 72 123 98 150 113 186 C 132 148 160 124 200 115 C 173 154 160 195 162 237 C 166 307 185 368 216 409 Z"')+ coloringContour("giraffe-neck","M 520 430 C 512 360 502 292 498 236 L 560 224 C 572 288 582 358 592 424 C 570 442 538 444 520 430 Z","#f7c948")+
coloringPart("path","cherry-canopy",'d="M 4 139 C 1 98 32 72 70 82 C 88 43 136 38 161 72 C 196 43 246 61 253 102 C 291 111 305 153 281 183 C 256 208 220 207 195 186 C 162 214 116 204 98 169 C 65 190 20 177 4 139 Z"')+ coloringContour("giraffe-head","M 484 240 C 470 212 484 184 512 178 C 542 172 566 190 568 218 C 552 240 514 250 484 240 Z","#f7c948")+
coloringFlower(73,106,"cherry-blossom-a",.52,2)+coloringFlower(145,80,"cherry-blossom-b",.5,3)+coloringFlower(212,124,"cherry-blossom-c",.48,3)+ coloringFlat("giraffe-muzzle","M 484 208 C 460 202 442 210 436 226 C 450 240 472 244 490 238 Z","#f2b38f")+
coloringLine("path",'d="M 73 225 C 89 205 102 194 113 186 M 162 237 C 145 215 130 199 113 186" fill="none"',2) coloringGroup("giraffe-detail",
)+ coloringCircle("giraffe-spot-a",556,414,18,"#8b5a3c")+
coloringFigure("pond-koi", coloringCircle("giraffe-spot-b",610,408,20,"#8b5a3c")+
coloringPart("path","koi-body",'d="M 433 514 C 469 478 538 479 576 516 C 537 550 469 549 433 514 Z"',3)+ coloringCircle("giraffe-spot-c",660,420,17,"#8b5a3c")+
coloringPart("path","koi-tail",'d="M 433 514 L 391 484 L 399 546 Z"',3)+ coloringCircle("giraffe-spot-d",624,464,16,"#8b5a3c")+
coloringPart("path","koi-patch",'d="M 485 492 C 500 507 502 531 487 545 C 508 552 530 548 548 538 C 533 518 534 499 550 489 C 528 483 506 484 485 492 Z"',3)+ coloringCircle("giraffe-spot-e",524,304,15,"#8b5a3c")+
coloringDot(554,513,4,3)+ coloringCircle("giraffe-spot-f",540,364,14,"#8b5a3c")+
coloringLine("path",'d="M 415 558 C 457 540 508 548 548 564 M 237 540 C 277 522 325 527 362 547" fill="none"',3) coloringCircle("giraffe-spot-g",512,252,12,"#8b5a3c")+
)+ coloringFlat("giraffe-horn-left","M 502 178 C 498 158 504 144 516 138 C 524 152 520 168 514 180 Z","#8b5a3c")+
coloringPart("path","stone-lantern-cap",'d="M 450 353 L 484 316 L 518 353 Z"',3)+ coloringFlat("giraffe-horn-right","M 540 176 C 540 156 548 142 560 138 C 564 154 558 168 550 180 Z","#8b5a3c")+
coloringPart("path","stone-lantern-body",'d="M 461 354 H 507 L 517 414 L 506 431 H 462 L 451 414 Z"',3)+ coloringFlat("giraffe-ear","M 566 206 C 588 194 610 196 620 210 C 604 224 580 224 566 218 Z","#f2b38f")+
coloringPart("path","stone-lantern-post",'d="M 475 429 H 494 V 523 H 475 Z"',3) coloringDot(516,206,6),2)+
)} coloringLine("M 444 222 C 448 232 456 238 466 240",2)+
coloringLine("M 532 448 C 566 462 634 466 682 456 M 512 292 C 528 298 544 306 554 312",3))+
coloringGroup("zebra",
coloringFlat("zebra-far-leg","M 318 470 L 318 558 C 318 572 348 572 348 558 L 348 470 Z","#ffffff")+
coloringFlat("zebra-near-leg","M 254 474 L 254 566 C 254 580 288 580 288 566 L 288 474 Z","#ffffff")+
coloringFlat("zebra-tail","M 362 438 C 382 450 394 468 398 490 C 386 486 372 474 362 456 Z","#ffffff")+
coloringContour("zebra-body","M 234 450 C 234 410 272 386 318 386 C 364 386 396 410 396 450 C 396 480 364 496 316 496 C 268 496 234 480 234 450 Z","#ffffff")+
coloringContour("zebra-neck","M 232 434 C 224 392 214 356 208 328 L 256 312 C 268 352 280 392 290 428 C 272 444 248 446 232 434 Z","#ffffff")+
coloringContour("zebra-head","M 194 330 C 178 304 190 276 216 270 C 244 264 266 282 268 308 C 252 330 220 340 194 330 Z","#ffffff")+
coloringFlat("zebra-muzzle","M 194 298 C 172 292 154 300 148 316 C 162 330 182 334 200 328 Z","#26344a")+
coloringGroup("zebra-stripes",
coloringFlat("stripe-a","M 264 398 C 270 424 270 456 264 490 L 284 492 C 290 456 290 424 284 396 Z","#26344a")+
coloringFlat("stripe-b","M 306 388 C 312 416 312 452 306 494 L 326 494 C 332 452 332 416 326 388 Z","#26344a")+
coloringFlat("stripe-c","M 348 392 C 354 418 354 452 348 488 L 366 484 C 372 450 372 418 366 392 Z","#26344a")+
coloringFlat("stripe-mane","M 216 272 C 228 304 242 342 254 376 L 274 368 C 262 334 248 298 236 268 Z","#26344a")+
coloringFlat("zebra-ear","M 266 292 C 288 280 310 282 320 296 C 304 310 280 310 266 304 Z","#ffffff")+
coloringDot(220,296,6),2)+
coloringLine("M 156 312 C 160 322 168 328 178 330",2)+
coloringLine("M 244 448 C 278 462 348 466 392 456 M 220 340 C 234 348 246 356 254 362",3))+
coloringGroup("grass-front",
coloringFlat("grass-tuft-a","M 440 566 C 434 534 442 508 460 490 C 470 514 468 544 456 566 Z","#3fae6a")+
coloringGroup("grass-extra",
coloringFlat("grass-tuft-b","M 738 570 C 732 540 740 514 758 496 C 768 520 766 548 754 570 Z","#3fae6a"),2)+
coloringLine("M 500 586 C 508 556 512 536 510 516 M 530 590 C 540 560 544 540 544 520 M 692 584 C 700 554 704 534 702 514",3))
);
}

View file

@ -1,162 +1,185 @@
/* teach — nieuwe kleurboekatlas voor groep 1-2 */ /* teach kleurplaten voor groep 1-2
Vijf platen, één per thema. Uitgangspunten voor deze band: weinig maar grote
vakken op tier 1, alles ruim genoeg voor een vingertop, en onderdelen die
tégen elkaar aan liggen op een gedeelde naad in plaats van over elkaar heen.
Daardoor loopt er nooit een lijn door een figuur heen. */
"use strict"; "use strict";
function proButterfly(){return coloringScene( function plateButterfly(){
coloringPart("rect","butterfly-paper",'x="4" y="4" width="792" height="592" rx="28"')+ /* van achter naar voren getekend: lucht, wolken, wei, klaproos, dan de
coloringPart("path","butterfly-meadow",'d="M 4 463 C 155 424 278 446 398 475 C 528 506 652 474 796 438 L 796 596 L 4 596 Z"')+ vlinder. Onderdelen mogen elkaar overlappen - een leeg vak is papierwit en
coloringFigure("monarch-butterfly", dekt dus af wat erachter ligt. */
coloringPart("path","butterfly-hindwing-left",'d="M 376 287 C 327 299 273 331 253 386 C 236 433 254 486 293 500 C 335 515 370 467 389 401 L 397 310 C 393 294 387 286 376 287 Z"')+ return coloringScene("butterfly",
coloringPart("path","butterfly-hindwing-right",'d="M 424 287 C 473 299 527 331 547 386 C 564 433 546 486 507 500 C 465 515 430 467 411 401 L 403 310 C 407 294 413 286 424 287 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringPart("path","butterfly-forewing-left",'d="M 382 303 C 340 279 277 257 231 208 C 190 164 191 104 232 79 C 276 52 335 91 369 153 C 394 198 397 253 392 286 C 390 299 388 305 382 303 Z"')+ coloringFlat("cloud-left",coloringCloudPath(58,84,186,62),"#ffffff")+
coloringPart("path","butterfly-forewing-right",'d="M 418 303 C 460 279 523 257 569 208 C 610 164 609 104 568 79 C 524 52 465 91 431 153 C 406 198 403 253 408 286 C 410 299 412 305 418 303 Z"')+ coloringGroup("cloud-extra",coloringFlat("cloud-right",coloringCloudPath(452,50,148,50),"#ffffff"),2)+
coloringPart("path","forewing-cell-left",'d="M 359 171 C 331 118 286 92 254 108 C 225 124 229 166 259 197 C 287 225 326 241 368 259 C 371 225 371 194 359 171 Z"',2)+ coloringCircle("sun",702,108,52,"#f7c948","coloring-contour")+
coloringPart("path","forewing-cell-right",'d="M 441 171 C 469 118 514 92 546 108 C 575 124 571 166 541 197 C 513 225 474 241 432 259 C 429 225 429 194 441 171 Z"',2)+ coloringGroup("sun-rays",
coloringPart("path","hindwing-eye-left",'d="M 294 383 C 312 353 350 356 364 387 C 378 420 355 453 324 451 C 294 449 278 411 294 383 Z"',2)+ coloringLine("M 702 34 L 702 12 M 776 108 L 796 108 M 754 56 L 770 40 M 754 160 L 770 176 M 650 56 L 634 40",2),2)+
coloringPart("path","hindwing-eye-right",'d="M 506 383 C 488 353 450 356 436 387 C 422 420 445 453 476 451 C 506 449 522 411 506 383 Z"',2)+ coloringFlat("meadow","M 4 452 C 150 420 290 436 420 460 C 540 482 670 470 796 440 L 796 596 L 4 596 Z","#3fae6a")+
coloringPart("path","butterfly-abdomen",'d="M 386 252 C 383 305 386 386 400 454 C 414 386 417 305 414 252 C 407 241 393 241 386 252 Z"')+ coloringGroup("poppy",
coloringPart("path","butterfly-thorax",'d="M 377 205 C 377 175 387 158 400 158 C 413 158 423 175 423 205 C 423 235 414 258 400 267 C 386 258 377 235 377 205 Z"')+ coloringFlat("poppy-stem","M 626 470 C 620 506 620 536 624 566 L 654 566 C 648 536 648 506 652 470 Z","#3fae6a")+
coloringPart("path","butterfly-head",'d="M 379 142 C 379 116 388 101 400 101 C 412 101 421 116 421 142 C 421 163 412 176 400 176 C 388 176 379 163 379 142 Z"')+ coloringFlat("poppy-leaf","M 650 508 C 690 486 732 492 750 518 C 726 546 684 548 650 528 Z","#3fae6a")+
coloringDot(391,136,3)+coloringDot(409,136,3)+ coloringContour("poppy-petal-top",coloringPetalPath(640,410,-90,72,46),"#e0554d")+
coloringLine("path",'d="M 386 113 C 368 83 347 68 325 72 M 414 113 C 432 83 453 68 475 72 M 386 286 H 414 M 388 329 H 412 M 391 371 H 409" fill="none"',2)+ coloringContour("poppy-petal-upper-left",coloringPetalPath(640,410,-162,72,46),"#e0554d")+
coloringLine("path",'d="M 382 216 C 349 201 313 181 279 151 M 418 216 C 451 201 487 181 521 151 M 379 322 C 347 353 329 389 323 429 M 421 322 C 453 353 471 389 477 429 M 380 225 L 350 249 M 420 225 L 450 249" fill="none"',3) coloringContour("poppy-petal-upper-right",coloringPetalPath(640,410,-18,72,46),"#e0554d")+
)+ coloringContour("poppy-petal-lower-left",coloringPetalPath(640,410,126,68,44),"#e0554d")+
coloringFigure("butterfly-flowers", coloringContour("poppy-petal-lower-right",coloringPetalPath(640,410,54,68,44),"#e0554d")+
coloringFlower(112,449,"butterfly-flower-left",.68,2)+ coloringCircle("poppy-heart",640,410,26,"#f7c948","coloring-contour")+
coloringFlower(688,449,"butterfly-flower-right",.68,3)+ coloringGroup("poppy-seeds",coloringLine("M 640 392 L 640 428 M 622 410 L 658 410 M 627 397 L 653 423 M 653 397 L 627 423",2),2))+
coloringLine("path",'d="M 112 469 V 548 M 688 469 V 548 M 112 511 C 91 494 73 499 66 515 M 688 511 C 709 494 727 499 734 515" fill="none"',2) coloringGroup("butterfly",
) coloringContour("wing-lower-left","M 334 300 C 306 320 268 338 240 366 C 208 398 214 438 252 444 C 296 450 330 412 340 366 C 342 344 340 320 334 300 Z","#f7c948")+
)} coloringContour("wing-lower-right","M 346 300 C 374 320 412 338 440 366 C 472 398 466 438 428 444 C 384 450 350 412 340 366 C 338 344 340 320 346 300 Z","#f7c948")+
coloringContour("wing-upper-left","M 336 236 C 300 190 244 148 190 146 C 138 144 112 186 128 232 C 144 278 200 306 258 300 C 300 296 326 274 336 236 Z","#f28c3c")+
coloringContour("wing-upper-right","M 344 236 C 380 190 436 148 490 146 C 542 144 568 186 552 232 C 536 278 480 306 422 300 C 380 296 354 274 344 236 Z","#f28c3c")+
coloringContour("butterfly-body","M 322 214 C 312 244 314 288 326 316 C 330 348 334 380 340 404 C 346 380 350 348 354 316 C 366 288 368 244 358 214 C 354 196 326 196 322 214 Z","#8b5a3c")+
coloringCircle("butterfly-head",340,196,22,"#8b5a3c","coloring-contour")+
coloringLine("M 330 178 C 316 148 296 130 270 122 M 350 178 C 364 148 384 130 410 122")+
coloringDot(270,122,7)+coloringDot(410,122,7)+
coloringGroup("butterfly-marks",
coloringEllipse("spot-upper-left",234,216,40,30,"#e0554d")+
coloringEllipse("spot-upper-right",446,216,40,30,"#e0554d")+
coloringCircle("spot-lower-left",278,392,24,"#e0554d")+
coloringCircle("spot-lower-right",402,392,24,"#e0554d")+
coloringCircle("spot-tip-left",172,246,16,"#7557c8")+
coloringCircle("spot-tip-right",508,246,16,"#7557c8")+
coloringDot(332,192,4)+coloringDot(348,192,4),2)+
coloringLine("M 326 260 L 354 260 M 328 296 L 352 296 M 332 336 L 348 336",2)+
coloringLine("M 178 200 C 216 214 258 236 296 264 M 502 200 C 464 214 422 236 384 264 M 256 404 C 284 396 310 384 330 370 M 424 404 C 396 396 370 384 350 370",3))+
coloringGroup("grass-tufts",
coloringLine("M 96 508 C 104 484 108 472 106 456 M 118 512 C 128 490 132 476 132 462 M 140 516 C 146 494 148 480 146 466")+
coloringLine("M 716 494 C 724 470 728 458 726 442 M 738 498 C 748 476 752 462 752 448",2),2)
);
}
function proAutumn(){return coloringScene( function plateAutumn(){
coloringPart("rect","autumn-paper",'x="4" y="4" width="792" height="592" rx="28"')+ /* de stam eerst, de kroon eroverheen: de bovenkant van de stam verdwijnt
coloringPart("path","autumn-ground",'d="M 4 451 C 153 414 285 437 402 466 C 532 498 655 468 796 430 L 796 596 L 4 596 Z"')+ achter het bladerdek, zoals bij een echte boom */
coloringFigure("oak-tree", return coloringScene("autumn",
coloringPart("path","oak-trunk",'d="M 315 526 C 337 475 348 419 348 356 C 348 300 333 250 306 204 C 340 224 366 252 381 288 C 388 230 379 185 354 148 C 393 169 420 202 433 246 C 459 209 495 184 541 176 C 501 212 478 256 471 310 C 494 286 521 272 550 270 C 512 307 488 351 483 402 C 480 452 495 493 519 526 C 451 511 384 512 315 526 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringPart("path","oak-crown",'d="M 120 244 C 94 203 116 154 159 144 C 158 96 203 66 246 86 C 272 47 328 45 356 84 C 394 50 455 58 476 104 C 517 76 578 96 589 143 C 635 143 665 187 648 228 C 681 262 657 312 615 320 C 602 363 548 383 513 354 C 477 390 417 379 397 337 C 361 375 300 365 281 321 C 242 348 190 330 182 286 C 150 290 128 274 120 244 Z"')+ coloringCircle("sun",114,112,48,"#f7c948","coloring-contour")+
coloringLine("path",'d="M 351 355 C 339 303 325 254 306 204 M 381 288 C 372 339 372 390 380 440 M 433 246 C 421 307 419 367 427 429 M 471 310 C 452 350 441 390 440 437 M 381 288 C 352 251 330 226 306 204 M 433 246 C 465 220 505 191 541 176" fill="none"',2) coloringFlat("far-hill","M 4 412 C 130 378 246 384 356 406 C 468 428 606 420 796 384 L 796 468 L 4 468 Z","#2fb7a8")+
)+ coloringFlat("meadow","M 4 468 L 796 468 L 796 596 L 4 596 Z","#3fae6a")+
[[154,398,-25],[228,454,18],[588,411,-18],[666,468,32],[91,502,12],[535,491,-37]].map(([x,y,a],i)=> coloringGroup("oak",
coloringLeaf("oak-leaf-"+i,x,y,a,.74,i<2?2:3)).join("")+ coloringContour("oak-trunk","M 322 468 C 344 412 358 348 362 260 L 442 260 C 446 348 460 412 482 468 Z","#8b5a3c")+
coloringLine("path",'d="M 147 398 l 14 0 M 221 454 l 14 0 M 581 411 l 14 0 M 659 468 l 14 0" fill="none"',3) coloringContour("oak-crown","M 402 62 C 474 62 528 94 548 140 C 606 154 622 214 586 254 C 600 296 558 332 512 322 C 478 350 420 352 402 324 C 384 352 326 350 292 322 C 246 332 204 296 218 254 C 182 214 198 154 256 140 C 276 94 330 62 402 62 Z","#f28c3c")+
)} coloringGroup("oak-clusters",
coloringFlat("cluster-left","M 262 214 C 288 190 330 192 348 216 C 328 248 284 250 262 234 Z","#f7c948")+
coloringFlat("cluster-right","M 456 208 C 482 184 524 186 542 210 C 522 242 478 244 456 228 Z","#f7c948")+
coloringFlat("cluster-mid","M 356 132 C 382 108 424 110 442 134 C 422 166 378 168 356 150 Z","#e0554d")+
coloringFlat("cluster-low","M 340 268 C 366 244 408 246 426 270 C 406 302 362 304 340 288 Z","#e0554d"),2)+
coloringLine("M 402 324 C 400 380 398 424 394 468 M 428 316 C 432 372 436 420 442 468",2)+
coloringLine("M 384 300 C 356 288 330 272 310 254 M 420 300 C 448 288 474 272 494 254 M 402 240 L 402 320",3))+
coloringGroup("fallen-leaves",
coloringFlat("leaf-a","M 148 512 C 156 486 180 470 206 474 C 224 478 234 492 232 510 C 210 534 168 534 148 512 Z","#f28c3c")+
coloringFlat("leaf-b","M 556 532 C 564 506 588 490 614 494 C 632 498 642 512 640 530 C 618 554 576 554 556 532 Z","#e0554d")+
coloringGroup("leaf-extra",
coloringFlat("leaf-c","M 292 556 C 300 532 322 518 346 522 C 362 526 372 538 370 554 C 350 576 310 576 292 556 Z","#f7c948")+
coloringFlat("leaf-d","M 664 476 C 672 452 694 438 718 442 C 734 446 744 458 742 474 C 722 496 682 496 664 476 Z","#f28c3c"),2)+
coloringLine("M 190 512 L 190 476 M 598 532 L 598 496 M 332 556 L 332 524",3))+
coloringGroup("birds",
coloringLine("M 606 128 C 620 116 632 116 644 126 C 656 116 668 116 682 128")+
coloringLine("M 650 176 C 662 166 672 166 682 174 C 692 166 702 166 714 176",2),2)
);
}
function proCake(){return coloringScene( function plateCake(){
coloringPart("rect","cake-paper",'x="4" y="4" width="792" height="592" rx="28"')+ /* de drie lagen liggen op vaste hoogtes: de naden zijn rechte lijnen die de
coloringPart("path","cake-table",'d="M 4 447 H 796 V 596 H 4 Z"')+ lagen delen, dus er ontstaat nergens een dubbele contour */
coloringFigure("layer-cake", return coloringScene("cake",
coloringPart("path","cake-stand-base",'d="M 277 548 C 305 520 496 520 523 548 L 509 576 H 291 Z"')+ coloringBackdrop("wall","#f2b38f")+
coloringPart("path","cake-stand-stem",'d="M 365 458 H 435 C 431 498 447 522 475 535 H 325 C 353 522 369 498 365 458 Z"',2)+ coloringFlat("table","M 4 496 L 796 496 L 796 596 L 4 596 Z","#8b5a3c")+
coloringPart("path","cake-platter",'d="M 180 439 C 236 421 564 421 620 439 C 594 469 206 469 180 439 Z"')+ coloringGroup("balloons",
coloringPart("path","cake-bottom",'d="M 220 332 C 291 315 509 315 580 332 V 430 C 535 454 265 454 220 430 Z"')+ coloringCircle("balloon-left",112,148,48,"#e0554d","coloring-contour")+
coloringPart("path","cake-bottom-icing",'d="M 220 332 C 291 315 509 315 580 332 V 363 C 553 348 532 378 500 361 C 468 343 449 380 414 361 C 381 343 361 380 326 361 C 293 343 268 375 220 358 Z"',2)+ coloringCircle("balloon-right",692,124,44,"#3b7dd8","coloring-contour")+
coloringPart("path","cake-middle",'d="M 275 230 C 327 217 473 217 525 230 V 326 C 486 341 314 341 275 326 Z"')+ coloringLine("M 112 196 C 104 244 108 288 124 330 M 692 168 C 700 216 696 258 682 296"))+
coloringPart("path","cake-middle-icing",'d="M 275 230 C 327 217 473 217 525 230 V 259 C 495 243 472 271 444 255 C 415 239 391 270 361 254 C 332 238 310 267 275 253 Z"',2)+ coloringGroup("cake",
coloringPart("path","cake-top",'d="M 330 147 C 363 138 437 138 470 147 V 224 C 440 236 360 236 330 224 Z"')+ coloringContour("cake-base","M 250 400 L 550 400 C 556 428 556 452 550 470 L 250 470 C 244 452 244 428 250 400 Z","#f7c948")+
coloringPart("path","cake-top-icing",'d="M 330 147 C 363 138 437 138 470 147 V 174 C 444 160 428 185 400 169 C 373 154 355 183 330 170 Z"',2)+ coloringContour("cake-middle","M 286 336 L 514 336 C 520 360 520 382 514 400 L 286 400 C 280 382 280 360 286 336 Z","#f2b38f")+
[356,400,444].map((x,i)=>coloringPart("path","cake-candle-"+i,`d="M ${x-9} 80 H ${x+9} V 143 H ${x-9} Z"`,i?2:1)).join("")+ coloringContour("cake-top","M 320 282 L 480 282 C 486 304 486 322 480 336 L 320 336 C 314 322 314 304 320 282 Z","#e0554d")+
[356,400,444].map((x,i)=>coloringPart("path","cake-flame-"+i,`d="M ${x} 78 C ${x-18} 61 ${x-12} 38 ${x} 23 C ${x+13} 41 ${x+17} 62 ${x} 78 Z"`,i===2?3:2)).join("")+ coloringFlat("cake-stand","M 300 470 L 500 470 C 494 484 480 492 462 496 L 338 496 C 320 492 306 484 300 470 Z","#7557c8")+
coloringLine("path",'d="M 347 95 L 365 110 M 391 95 L 409 110 M 435 95 L 453 110 M 250 398 C 313 418 487 418 550 398 M 302 297 C 351 314 449 314 498 297" fill="none"',3) coloringGroup("cake-icing",
)+ coloringFlat("icing-base","M 250 400 L 550 400 C 540 416 526 420 512 410 C 498 424 482 424 468 410 C 454 424 438 424 424 410 C 410 424 394 424 380 410 C 366 424 350 424 336 410 C 322 424 306 424 292 410 C 278 420 262 416 250 400 Z","#ffffff")+
coloringStars([[112,146,14],[687,119,13],[121,332,11],[672,339,12]],"cake-confetti",3) coloringFlat("icing-top","M 320 282 L 480 282 C 470 298 456 302 442 292 C 428 306 412 306 398 292 C 384 306 368 306 354 292 C 340 302 326 298 320 282 Z","#ffffff"),2)+
)} coloringLine("M 286 368 L 514 368 M 250 436 L 550 436",3))+
coloringGroup("candles",
coloringFlat("candle-left","M 344 216 L 366 216 L 366 282 L 344 282 Z","#3fae6a")+
coloringFlat("candle-mid","M 389 200 L 411 200 L 411 282 L 389 282 Z","#3b7dd8")+
coloringFlat("candle-right","M 434 216 L 456 216 L 456 282 L 434 282 Z","#7557c8")+
coloringFlat("flame-left","M 355 216 C 344 204 346 188 355 176 C 364 188 366 204 355 216 Z","#f28c3c")+
coloringFlat("flame-mid","M 400 200 C 389 188 391 170 400 158 C 409 170 411 188 400 200 Z","#f28c3c")+
coloringFlat("flame-right","M 445 216 C 434 204 436 188 445 176 C 454 188 456 204 445 216 Z","#f28c3c"))+
coloringGroup("confetti",
coloringLine("M 168 300 L 184 316 M 184 300 L 168 316 M 620 372 L 636 388 M 636 372 L 620 388 M 132 412 L 148 428 M 148 412 L 132 428")+
coloringLine("M 596 216 L 612 232 M 612 216 L 596 232 M 216 232 L 232 248 M 232 232 L 216 248",3),2)
);
}
function proFarmyard(){return coloringScene( function platePicnic(){
coloringPart("rect","farm-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("forest-picnic",
coloringPart("path","farm-pasture",'d="M 4 426 C 143 388 281 410 402 442 C 535 477 661 442 796 405 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringFigure("timber-barn", coloringFlat("far-trees","M 4 356 C 96 300 148 336 214 292 C 268 256 324 306 392 280 C 458 254 512 302 578 282 C 650 260 716 306 796 288 L 796 452 L 4 452 Z","#2fb7a8")+
coloringPart("path","barn-wall",'d="M 41 239 H 259 V 488 H 41 Z"',2)+ coloringFlat("meadow","M 4 452 L 796 452 L 796 596 L 4 596 Z","#3fae6a")+
coloringPart("path","barn-roof",'d="M 20 239 L 150 104 L 280 239 L 253 260 L 150 154 L 47 260 Z"')+ coloringGroup("tree-left",
coloringPart("path","barn-door-left",'d="M 72 323 H 147 V 488 H 72 Z"',2)+ coloringContour("tree-left-crown","M 60 268 C 30 244 38 200 76 190 C 78 148 122 128 154 148 C 186 126 232 144 238 182 C 274 196 278 244 244 266 L 168 266 L 168 292 L 128 292 L 128 266 L 60 268 Z","#3fae6a")+
coloringPart("path","barn-door-right",'d="M 147 323 H 222 V 488 H 147 Z"',2)+ coloringFlat("tree-left-trunk","M 118 452 C 122 400 124 344 128 292 L 168 292 C 172 344 176 400 182 452 Z","#8b5a3c"))+
coloringPart("path","barn-window",'d="M 118 213 H 182 V 276 H 118 Z"',3)+ coloringGroup("tree-right",
coloringLine("path",'d="M 72 323 L 147 405 L 72 488 M 222 323 L 147 405 L 222 488 M 150 213 V 276 M 118 244 H 182" fill="none"',3) coloringContour("tree-right-crown","M 596 232 C 566 208 574 164 612 154 C 614 112 658 92 690 112 C 722 90 768 108 774 146 C 796 156 796 206 776 224 L 706 224 L 706 250 L 666 250 L 666 224 L 596 232 Z","#3fae6a")+
)+ coloringFlat("tree-right-trunk","M 656 452 C 660 400 662 300 666 250 L 706 250 C 710 300 712 400 718 452 Z","#8b5a3c"))+
coloringFigure("holstein-cow", coloringGroup("blanket",
coloringPart("path","cow-tail",'d="M 351 312 C 324 302 304 282 295 252 C 286 222 298 198 314 207 C 327 215 319 236 323 252 C 328 277 345 285 365 291 L 374 313 Z"',2)+ coloringContour("picnic-blanket","M 226 476 L 596 476 L 664 574 L 158 574 Z","#e0554d")+
[365,439,515,566].map((x,i)=>coloringPart("path","cow-leg-"+i,`d="M ${x} 407 C ${x-2} 448 ${x-4} 491 ${x+1} 529 C ${x+13} 535 ${x+31} 535 ${x+43} 529 L ${x+39} 512 C ${x+31} 476 ${x+34} 440 ${x+29} 402 Z"`,i>1?2:1)).join("")+ coloringGroup("blanket-check",
coloringPart("path","cow-silhouette",'d="M 321 340 C 323 284 376 250 457 250 C 518 249 558 264 590 284 C 612 266 632 246 655 230 C 680 212 716 219 735 242 C 754 264 750 296 730 314 C 717 326 700 332 682 331 C 670 347 651 359 627 363 C 625 424 573 461 488 461 C 395 461 315 421 321 340 Z"')+ coloringLine("M 300 476 L 258 574 M 380 476 L 358 574 M 460 476 L 458 574 M 540 476 L 558 574 M 200 508 L 626 508 M 178 542 L 646 542"),2))+
coloringPart("path","cow-muzzle",'d="M 678 277 C 698 261 733 267 745 290 C 750 315 729 334 701 334 C 675 333 661 307 678 277 Z"',2)+ coloringGroup("basket",
coloringPart("path","cow-ear-back",'d="M 667 239 C 641 215 614 214 603 229 C 612 253 637 261 668 257 Z"',2)+ coloringContour("basket-body","M 254 486 L 372 486 C 366 528 358 552 344 566 L 282 566 C 268 552 260 528 254 486 Z","#8b5a3c")+
coloringPart("path","cow-ear-front",'d="M 709 229 C 733 203 761 202 773 218 C 765 245 740 256 710 249 Z"',2)+ coloringFlat("basket-handle","M 272 486 C 276 448 306 428 340 448 C 356 458 362 472 362 486 L 344 486 C 344 470 334 460 320 458 C 302 456 290 468 290 486 Z","#f28c3c")+
coloringPart("path","cow-horn-back",'d="M 666 228 C 648 204 650 184 669 174 C 680 194 678 212 666 228 Z"',3)+ coloringGroup("basket-weave",coloringLine("M 258 510 L 368 510 M 262 534 L 362 534",2),2))+
coloringPart("path","cow-horn-front",'d="M 711 222 C 722 194 741 183 758 192 C 751 214 735 226 711 236 Z"',3)+ coloringGroup("rabbit",
coloringPart("path","cow-udder",'d="M 446 444 C 464 422 510 421 530 445 C 524 474 511 487 491 487 C 468 487 452 474 446 444 Z"',2)+ coloringContour("rabbit-body","M 430 566 C 410 540 412 504 436 484 C 460 464 496 468 512 492 C 528 516 524 548 506 566 Z","#f2b38f")+
coloringPart("path","cow-patch-shoulder",'d="M 354 292 C 379 268 421 260 451 273 C 433 303 431 340 449 371 C 410 383 373 365 356 335 C 348 320 347 305 354 292 Z"',2)+ coloringCircle("rabbit-head",530,458,42,"#f2b38f","coloring-contour")+
coloringPart("path","cow-patch-flank",'d="M 505 273 C 541 267 574 278 600 299 C 581 326 578 360 596 390 C 558 406 522 389 511 358 C 499 325 500 297 505 273 Z"',3)+ coloringContour("rabbit-ear-left","M 508 424 C 494 388 496 348 512 322 C 528 344 532 386 524 420 Z","#f2b38f")+
coloringDot(711,263,4)+coloringDot(692,299,3)+coloringDot(729,299,3)+ coloringContour("rabbit-ear-right","M 548 422 C 556 386 574 352 596 336 C 600 364 586 402 564 428 Z","#f2b38f")+
coloringLine("path",'d="M 686 316 C 701 319 720 319 737 313 M 378 529 H 408 M 452 529 H 482 M 528 529 H 558 M 579 529 H 609" fill="none"',2) coloringCircle("rabbit-tail",418,532,20,"#ffffff","coloring-contour")+
) coloringGroup("rabbit-face",
)} coloringCircle("rabbit-inner-ear-left",513,378,10,"#e0554d")+
coloringCircle("rabbit-inner-ear-right",572,386,10,"#e0554d")+
coloringDot(518,452,5)+coloringDot(548,452,5)+
coloringFlat("rabbit-nose","M 526 470 C 532 464 540 464 546 470 C 540 478 532 478 526 470 Z","#e0554d"),2)+
coloringLine("M 522 482 C 528 490 540 490 546 482 M 500 470 L 470 462 M 500 480 L 470 484 M 570 470 L 600 462 M 570 480 L 600 484",3))+
coloringGroup("apples",
coloringCircle("apple-left",404,500,22,"#e0554d","coloring-contour")+
coloringCircle("apple-right",600,510,22,"#f7c948","coloring-contour")+
coloringLine("M 404 478 C 402 468 404 462 410 458 M 600 488 C 598 478 600 472 606 468"),2)
);
}
function proRainbowWeather(){return coloringScene( function plateExcavator(){
coloringPart("rect","weather-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("construction-site",
coloringPart("path","weather-hill",'d="M 4 447 C 145 407 279 431 401 462 C 534 496 660 462 796 423 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringFigure("rainbow", coloringFlat("far-hill","M 4 372 C 128 336 236 344 344 366 C 456 388 596 380 796 344 L 796 434 L 4 434 Z","#2fb7a8")+
coloringPart("path","rainbow-outer",'d="M 57 405 C 92 194 254 92 400 92 C 548 92 710 194 743 405 H 683 C 652 236 526 153 400 153 C 276 153 150 236 117 405 Z"',2)+ coloringFlat("sand","M 4 434 L 796 434 L 796 596 L 4 596 Z","#f7c948")+
coloringPart("path","rainbow-middle",'d="M 117 405 C 150 236 276 153 400 153 C 526 153 652 236 683 405 H 623 C 595 279 495 212 400 212 C 306 212 205 279 177 405 Z"',2)+ coloringCircle("sun",108,110,46,"#f28c3c","coloring-contour")+
coloringPart("path","rainbow-inner",'d="M 177 405 C 205 279 306 212 400 212 C 495 212 595 279 623 405 H 563 C 539 323 468 270 400 270 C 332 270 260 323 237 405 Z"',3) coloringFlat("cloud",coloringCloudPath(548,84,190,66),"#ffffff")+
)+ coloringFlat("dirt-pile","M 596 528 C 632 470 682 448 730 456 C 772 464 792 494 796 528 Z","#8b5a3c")+
coloringFigure("child-in-raincoat", coloringGroup("excavator",
coloringPart("path","rain-boot-back",'d="M 355 485 H 389 L 397 548 C 373 555 346 554 326 548 C 327 530 342 523 358 523 Z"',2)+ coloringContour("track-frame","M 96 496 C 96 466 118 448 148 448 L 356 448 C 386 448 408 466 408 496 C 408 526 386 544 356 544 L 148 544 C 118 544 96 526 96 496 Z","#26344a")+
coloringPart("path","rain-boot-front",'d="M 420 485 H 454 L 466 548 C 443 556 415 555 395 548 C 396 529 412 522 428 522 Z"',2)+ coloringContour("excavator-body","M 132 356 L 372 356 L 372 448 L 132 448 Z","#f7c948")+
coloringPart("path","raincoat-body",'d="M 342 337 C 354 316 375 305 401 305 C 428 305 449 317 460 338 L 491 475 C 440 493 361 493 311 475 Z"')+ coloringContour("excavator-cab","M 164 246 L 320 246 L 320 356 L 164 356 Z","#e0554d")+
coloringPart("path","raincoat-arm",'d="M 448 343 C 474 353 497 376 510 409 L 479 429 C 464 402 448 388 431 380 Z"',2)+ coloringContour("boom","M 367 358 L 337 322 L 477 204 L 508 240 Z","#f28c3c")+
coloringPart("path","raincoat-hand",'d="M 476 423 C 488 410 511 413 520 428 C 526 445 511 459 494 455 C 479 452 471 437 476 423 Z"',2)+ coloringContour("dipper","M 474 231 L 510 213 L 584 363 L 548 381 Z","#f28c3c")+
coloringPart("path","child-head",'d="M 358 280 C 359 238 377 216 402 216 C 428 216 445 240 443 282 C 440 318 424 337 401 337 C 377 337 357 317 358 280 Z"')+ coloringContour("bucket","M 534 360 C 574 350 614 362 636 388 C 628 428 594 452 554 448 C 536 428 530 392 534 360 Z","#26344a")+
coloringPart("path","rain-hood",'d="M 337 268 C 337 218 363 189 402 189 C 442 189 468 219 466 270 C 452 251 445 237 441 220 C 421 235 389 238 361 221 C 358 240 351 255 337 268 Z"',2)+ coloringGroup("excavator-detail",
coloringDot(384,278,4)+coloringDot(419,278,4)+ coloringFlat("cab-window","M 186 268 L 298 268 L 298 328 L 186 328 Z","#2fb7a8")+
coloringLine("path",'d="M 388 304 Q 402 314 417 303 M 400 338 V 468 M 375 368 H 427 M 376 403 H 428 M 477 429 L 513 410" fill="none"',2) coloringCircle("track-wheel-left",158,496,26,"#f2b38f")+
)+ coloringCircle("track-wheel-mid",252,496,26,"#f2b38f")+
coloringFigure("umbrella", coloringCircle("track-wheel-right",346,496,26,"#f2b38f")+
coloringPart("path","umbrella-canopy",'d="M 224 220 C 260 114 369 64 481 87 C 568 104 631 164 647 239 C 612 214 576 210 541 229 C 508 197 469 194 434 217 C 402 187 362 184 328 209 C 296 192 258 196 224 220 Z"')+ coloringFlat("engine-hatch","M 152 376 L 232 376 L 232 428 L 152 428 Z","#f28c3c"),2)+
coloringLine("path",'d="M 434 217 C 432 166 447 121 481 87 M 328 209 C 349 151 397 105 481 87 M 541 229 C 541 174 525 124 481 87 M 434 217 V 442 C 434 470 454 483 472 470" fill="none"',2) coloringLine("M 132 400 L 372 400 M 348 302 L 372 292",2)+
)+ coloringLine("M 116 470 L 388 470 M 116 522 L 388 522 M 496 292 L 520 276",3))+
[[92,286],[141,338],[690,292],[727,355]].map(([x,y],i)=>coloringPart("path","rain-drop-"+i,`d="M ${x} ${y-18} C ${x-18} ${y+5} ${x-11} ${y+24} ${x} ${y+26} C ${x+12} ${y+23} ${x+18} ${y+5} ${x} ${y-18} Z"`,3)).join("") coloringGroup("cones",
)} coloringFlat("cone-left","M 32 566 L 62 496 L 92 566 Z","#f28c3c")+
coloringFlat("cone-right","M 456 574 L 486 504 L 516 574 Z","#f28c3c")+
function proConstruction(){return coloringScene( coloringLine("M 44 540 L 80 540 M 468 548 L 504 548",2),2)
coloringPart("rect","construction-paper",'x="4" y="4" width="792" height="592" rx="28"')+ );
coloringPart("path","construction-ground",'d="M 4 436 C 154 405 282 422 405 448 C 535 477 662 451 796 415 L 796 596 L 4 596 Z"')+ }
coloringFigure("tracked-excavator",
coloringPart("path","excavator-track",'d="M 98 438 C 107 390 139 368 182 368 H 428 C 475 368 506 397 510 444 C 508 492 475 520 430 520 H 177 C 131 520 100 489 98 438 Z"')+
coloringPart("path","excavator-track-pad",'d="M 139 441 C 144 415 161 403 185 403 H 419 C 444 403 461 419 465 443 C 462 468 445 483 421 483 H 181 C 157 483 141 467 139 441 Z"',2)+
coloringPart("path","excavator-chassis",'d="M 169 361 H 440 L 469 399 H 139 Z"')+
coloringPart("path","excavator-counterweight",'d="M 167 274 C 123 281 101 317 107 361 H 279 V 273 Z"',2)+
coloringPart("path","excavator-cab",'d="M 277 194 H 388 C 417 194 437 211 448 239 L 485 361 H 277 Z"')+
coloringPart("path","excavator-window-front",'d="M 347 216 H 386 C 404 216 416 228 423 247 L 444 313 H 347 Z"',2)+
coloringPart("path","excavator-window-side",'d="M 296 216 H 331 V 313 H 296 Z"',2)+
coloringPart("path","excavator-boom",'d="M 425 248 L 548 82 L 580 104 L 488 284 Z"')+
coloringPart("path","excavator-stick",'d="M 548 82 L 713 226 L 686 255 L 563 132 Z"',2)+
coloringPart("path","excavator-bucket",'d="M 685 224 C 739 225 778 264 780 310 C 766 348 735 374 687 385 L 713 350 L 675 320 L 651 278 Z"')+
coloringPart("circle","excavator-main-pivot",'cx="458" cy="278" r="25"',2)+
coloringPart("circle","excavator-elbow-pivot",'cx="560" cy="108" r="18"',3)+
coloringLine("path",'d="M 170 443 H 438 M 201 405 V 483 M 253 405 V 483 M 305 405 V 483 M 357 405 V 483 M 409 405 V 483 M 458 278 L 560 108 M 560 108 L 697 244" fill="none"',3)
)+
coloringPart("path","construction-cone",'d="M 65 520 L 91 424 L 117 520 Z"',3)+
coloringPart("path","construction-cone-base",'d="M 47 520 H 135 L 143 548 H 39 Z"',3)
)}
function proForestPicnic(){return coloringScene(
coloringPart("rect","picnic-paper",'x="4" y="4" width="792" height="592" rx="28"')+
coloringPart("path","picnic-ground",'d="M 4 427 C 144 390 281 413 401 444 C 535 478 663 444 796 405 L 796 596 L 4 596 Z"')+
coloringFigure("woodland-oak",
coloringPart("path","picnic-tree-trunk",'d="M 83 448 C 109 389 119 326 115 265 C 112 218 99 181 79 151 C 112 166 136 192 150 225 C 169 190 198 168 236 160 C 208 197 195 238 198 282 C 203 348 222 404 249 448 Z"',2)+
coloringPart("path","picnic-tree-crown",'d="M 31 171 C 10 126 39 83 83 83 C 100 40 153 27 186 61 C 221 26 280 42 292 90 C 337 100 351 151 322 181 C 298 215 249 217 221 189 C 190 222 137 215 116 179 C 85 201 47 196 31 171 Z"')
)+
coloringPart("path","picnic-blanket",'d="M 289 461 L 607 426 L 720 554 L 365 570 Z"',2)+
coloringFigure("wild-rabbit",
coloringPart("path","rabbit-tail",'d="M 411 349 C 377 328 342 343 339 376 C 338 405 369 422 397 408 C 419 397 426 368 411 349 Z"',2)+
coloringPart("path","rabbit-ear-back",'d="M 572 282 C 575 216 597 159 630 150 C 650 178 628 244 594 294 Z"',2)+
coloringPart("path","rabbit-ear-front",'d="M 535 281 C 526 211 541 148 573 132 C 598 157 584 230 558 293 Z"')+
coloringPart("path","rabbit-silhouette",'d="M 388 403 C 387 351 431 315 494 311 C 520 298 539 276 555 257 C 577 231 618 228 644 249 C 670 270 675 308 654 334 C 641 349 624 358 603 359 C 619 384 618 418 600 442 C 573 477 520 482 474 467 C 431 453 391 439 388 403 Z"')+
coloringPart("path","rabbit-hind-leg",'d="M 411 403 C 451 383 501 399 515 435 C 515 468 482 493 438 494 C 402 493 383 470 390 445 C 394 429 401 415 411 403 Z"',2)+
coloringPart("path","rabbit-chest",'d="M 558 355 C 582 372 589 407 573 435 C 555 452 530 448 518 430 C 512 405 526 374 558 355 Z"',3)+
coloringPart("path","rabbit-forepaw",'d="M 548 433 C 574 424 601 435 610 455 C 596 476 561 480 536 467 Z"',2)+
coloringDot(624,281,5)+
coloringLine("path",'d="M 647 307 l 13 3 l -11 7 M 649 318 C 656 322 664 321 670 317 M 432 494 H 486 M 563 469 H 608" fill="none"',2)
)+
coloringFigure("picnic-basket",
coloringPart("path","basket-body",'d="M 245 405 H 361 L 345 500 H 261 Z"',2)+
coloringLine("path",'d="M 266 407 C 269 340 337 339 341 407 M 254 444 H 353 M 273 409 L 283 497 M 311 408 L 311 500 M 345 411 L 334 497" fill="none"',3)
)
)}

View file

@ -1,187 +1,207 @@
/* teach — nieuwe kleurboekatlas voor groep 3-4 */ /* teach kleurplaten voor groep 3-4
Meer vakken dan bij groep 1-2 en wat meer binnenvorm, maar nog steeds ruime
vlakken. Alles van achter naar voren getekend. */
"use strict"; "use strict";
function proOcean(){return coloringScene( function plateOcean(){
coloringPart("rect","ocean-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("ocean",
coloringPart("path","ocean-floor",'d="M 4 472 C 131 433 258 448 391 477 C 529 506 660 486 796 443 L 796 596 L 4 596 Z"')+ coloringBackdrop("water","#3b7dd8")+
coloringFigure("reef-fish", coloringGroup("light-rays",coloringLine("M 120 8 L 210 250 M 200 8 L 268 250 M 610 8 L 540 250 M 690 8 L 596 250",3),3)+
coloringPart("path","fish-tail",'d="M 221 282 C 170 252 127 210 86 190 C 103 236 104 303 88 350 C 131 332 174 304 221 292 Z"')+ coloringFlat("deep-water","M 4 300 C 160 272 320 288 470 306 C 600 322 700 314 796 296 L 796 460 L 4 460 Z","#2fb7a8")+
coloringPart("path","fish-dorsal-fin",'d="M 288 207 C 325 159 374 151 411 190 C 365 190 328 201 297 226 Z"',2)+ coloringFlat("sand","M 4 460 C 150 436 300 452 448 468 C 580 482 700 474 796 452 L 796 596 L 4 596 Z","#f7c948")+
coloringPart("path","fish-ventral-fin",'d="M 310 350 C 348 384 397 382 421 346 C 379 357 344 359 310 350 Z"',3)+ coloringGroup("weeds",
coloringPart("path","fish-silhouette",'d="M 205 286 C 257 199 374 180 472 218 C 519 237 553 263 577 290 C 550 329 512 353 462 365 C 365 388 258 360 205 286 Z"')+ coloringFlat("weed-left","M 62 552 C 40 500 58 452 92 420 C 108 458 106 500 88 540 C 84 550 74 556 62 552 Z","#3fae6a")+
coloringPart("path","fish-gill-cover",'d="M 484 230 C 462 262 462 318 486 351 C 527 337 557 316 577 290 C 554 264 524 243 484 230 Z"',2)+ coloringFlat("weed-right","M 748 546 C 726 494 742 444 776 414 C 792 452 790 494 772 534 C 768 544 758 550 748 546 Z","#3fae6a")+
coloringPart("path","fish-side-patch",'d="M 332 212 C 311 253 310 317 334 365 C 367 372 399 371 430 365 C 410 316 409 255 429 203 C 396 197 362 201 332 212 Z"',2)+ coloringGroup("weed-extra",
coloringPart("path","fish-pectoral-fin",'d="M 474 286 C 447 290 423 310 417 340 C 450 344 480 324 490 299 Z"',3)+ coloringFlat("weed-mid","M 132 556 C 116 514 130 476 156 452 C 168 482 166 516 152 548 C 148 556 140 560 132 556 Z","#3fae6a"),2))+
coloringDot(528,270,6)+ coloringGroup("rocks",
coloringLine("path",'d="M 538 310 Q 555 319 569 306 M 241 286 C 275 271 300 271 327 286 M 286 211 C 273 247 273 322 290 355" fill="none"',3) coloringFlat("rock-left","M 96 566 C 106 528 138 508 172 516 C 202 524 218 548 216 566 Z","#8b5a3c")+
)+ coloringFlat("rock-right","M 604 570 C 616 536 648 518 680 526 C 708 534 722 554 720 570 Z","#8b5a3c"))+
coloringFigure("green-sea-turtle", coloringGroup("turtle",
coloringPart("path","turtle-rear-flipper",'d="M 453 414 C 418 403 391 416 381 445 C 409 466 443 457 473 434 Z"',2)+ coloringFlat("turtle-front-flipper","M 566 430 C 530 410 496 412 476 430 C 498 456 534 464 568 452 Z","#3fae6a")+
coloringPart("path","turtle-front-flipper-far",'d="M 601 372 C 634 335 676 320 707 338 C 690 377 655 399 612 410 Z"',2)+ coloringFlat("turtle-rear-flipper","M 648 456 C 672 474 700 476 718 466 C 704 442 678 432 650 436 Z","#3fae6a")+
coloringPart("path","turtle-front-flipper-near",'d="M 590 438 C 636 444 675 469 682 507 C 637 514 597 488 569 453 Z"')+ coloringContour("turtle-shell","M 544 434 C 550 386 586 358 628 362 C 670 366 698 398 694 442 C 664 468 610 472 568 460 C 552 456 544 446 544 434 Z","#f28c3c")+
coloringPart("path","turtle-shell",'d="M 409 414 C 416 342 478 305 551 316 C 614 326 646 369 633 423 C 620 478 563 500 503 484 C 445 469 407 449 409 414 Z"')+ coloringFlat("turtle-neck","M 686 404 C 700 396 714 396 726 402 L 722 434 C 710 438 696 436 686 428 Z","#3fae6a")+
coloringPart("path","turtle-neck",'d="M 615 374 C 639 355 663 345 686 347 L 700 400 C 672 407 646 416 625 431 Z"',2)+ coloringCircle("turtle-head",736,410,26,"#3fae6a","coloring-contour")+
coloringPart("path","turtle-head",'d="M 674 352 C 696 329 735 332 753 359 C 770 384 756 418 726 427 C 695 436 671 413 668 383 C 667 369 669 359 674 352 Z"')+ coloringGroup("turtle-detail",
coloringPart("path","turtle-shell-center",'d="M 476 344 C 520 323 576 336 602 374 C 615 416 587 459 540 467 C 492 469 449 441 448 402 C 449 376 459 356 476 344 Z"',2)+ coloringFlat("shell-plate-mid","M 592 400 C 610 384 638 384 656 400 C 652 428 620 438 594 428 Z","#f7c948")+
coloringDot(728,369,5)+ coloringFlat("shell-plate-left","M 556 428 C 562 402 574 388 590 382 C 588 408 580 428 566 442 Z","#f7c948")+
coloringLine("path",'d="M 498 335 C 482 370 482 430 503 466 M 563 333 C 585 369 588 427 570 461 M 454 391 C 498 371 557 372 612 397 M 451 426 C 498 447 557 449 602 430 M 721 401 C 733 405 743 403 750 397" fill="none"',3) coloringFlat("shell-plate-right","M 660 384 C 676 392 688 410 690 434 C 674 434 662 424 656 404 Z","#f7c948")+
)+ coloringDot(744,402,5),2)+
coloringPart("path","ocean-seagrass-a",'d="M 103 535 C 81 481 87 423 118 376 C 135 434 131 489 118 540 Z"',2)+ coloringLine("M 730 420 C 738 424 748 424 754 420",3))+
coloringPart("path","ocean-seagrass-b",'d="M 157 542 C 137 486 151 434 187 397 C 196 451 183 503 170 545 Z"',3) coloringGroup("fish",
)} coloringFlat("fish-tail","M 306 306 C 274 272 248 260 226 262 C 234 292 234 320 226 350 C 250 352 278 340 306 314 Z","#f28c3c")+
coloringFlat("fish-dorsal-fin","M 356 250 C 370 214 394 192 424 188 C 420 216 412 238 400 256 Z","#f28c3c")+
coloringFlat("fish-pelvic-fin","M 372 356 C 380 384 396 400 418 406 C 416 382 408 364 396 350 Z","#f28c3c")+
coloringContour("fish-body","M 300 306 C 320 254 368 224 426 224 C 488 224 536 258 554 306 C 536 354 488 388 426 388 C 368 388 320 358 300 306 Z","#e0554d")+
coloringGroup("fish-detail",
coloringFlat("fish-head-band","M 466 234 C 490 246 508 270 514 302 C 508 334 490 360 466 376 C 456 348 452 324 452 304 C 452 280 456 258 466 234 Z","#ffffff")+
coloringFlat("fish-mid-band","M 372 240 C 388 232 404 228 420 226 C 412 252 408 278 408 306 C 408 334 412 360 420 386 C 404 384 388 380 372 372 C 380 348 384 326 384 306 C 384 284 380 262 372 240 Z","#ffffff")+
coloringCircle("fish-eye",506,286,13,"#ffffff")+
coloringDot(508,286,6),2)+
coloringLine("M 528 258 C 540 278 544 296 544 306 C 544 318 540 336 528 354",2)+
coloringLine("M 322 300 C 340 292 356 288 372 286 M 322 316 C 340 322 356 326 372 328",3))+
coloringGroup("bubbles",
coloringCircle("bubble-a",600,238,20,"#ffffff","coloring-contour")+
coloringCircle("bubble-b",644,190,13,"#ffffff","coloring-contour")+
coloringCircle("bubble-c",616,152,9,"#ffffff","coloring-contour"),2)
);
}
function proGarden(){return coloringScene( function plateGarden(){
coloringPart("rect","garden-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("garden",
coloringPart("path","garden-soil",'d="M 4 418 C 149 380 280 403 401 435 C 535 470 661 437 796 399 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringFigure("roman-snail", coloringFlat("hedge","M 4 306 C 90 262 170 300 250 274 C 330 248 400 292 480 272 C 560 252 640 296 720 276 C 756 268 780 274 796 284 L 796 400 L 4 400 Z","#2fb7a8")+
coloringPart("path","snail-foot",'d="M 79 473 C 149 459 224 466 293 470 C 337 472 370 464 401 447 C 429 431 463 435 479 456 C 465 492 426 505 376 506 H 120 C 91 506 73 493 79 473 Z"')+ coloringFlat("lawn","M 4 400 L 796 400 L 796 596 L 4 596 Z","#3fae6a")+
coloringPart("path","snail-shell",'d="M 107 421 C 91 346 139 282 213 278 C 288 274 344 329 340 402 C 336 468 276 490 207 477 C 151 467 117 450 107 421 Z"')+ coloringGroup("flowers",
coloringPart("path","snail-head-neck",'d="M 331 438 C 347 386 371 355 408 351 C 446 347 474 372 476 410 C 479 447 448 475 409 474 C 381 474 359 460 331 438 Z"',2)+ coloringFlat("flower-stem-left","M 128 356 C 122 400 124 440 130 476 L 152 476 C 146 440 144 400 150 356 Z","#3fae6a")+
coloringDot(399,391,4)+coloringDot(441,392,4)+ coloringFlat("flower-leaf-left","M 148 410 C 182 390 218 396 234 420 C 212 444 176 446 148 428 Z","#3fae6a")+
coloringLine("path",'d="M 393 367 C 385 336 375 318 359 303 M 442 368 C 451 336 462 318 478 304 M 405 430 C 417 434 430 433 441 427 M 159 417 C 152 361 191 325 239 331 C 283 337 303 376 286 413 C 270 448 220 451 198 424 C 181 403 191 375 218 374 C 238 373 250 390 242 406" fill="none"',2) coloringContour("flower-petal-a",coloringPetalPath(140,336,-90,58,34),"#d85aa5")+
)+ coloringContour("flower-petal-b",coloringPetalPath(140,336,-18,56,32),"#d85aa5")+
coloringFigure("seven-spot-ladybird", coloringContour("flower-petal-c",coloringPetalPath(140,336,54,54,32),"#d85aa5")+
coloringPart("path","ladybird-leaf",'d="M 493 455 C 544 398 665 387 730 434 C 685 500 571 525 493 455 Z"',2)+ coloringContour("flower-petal-d",coloringPetalPath(140,336,126,54,32),"#d85aa5")+
coloringPart("path","ladybird-leg-back-a",'d="M 561 419 C 530 398 503 397 483 412 L 493 428 C 515 418 538 424 558 442 Z"',3)+ coloringContour("flower-petal-e",coloringPetalPath(140,336,198,56,32),"#d85aa5")+
coloringPart("path","ladybird-leg-back-b",'d="M 563 466 C 530 471 509 487 505 509 L 526 511 C 534 490 550 482 573 481 Z"',3)+ coloringCircle("flower-heart",140,336,20,"#f7c948","coloring-contour"))+
coloringPart("path","ladybird-leg-front-a",'d="M 677 414 C 705 392 733 392 752 408 L 741 425 C 719 415 697 422 680 439 Z"',3)+ coloringFlat("big-leaf","M 232 520 C 296 456 400 436 494 462 C 470 528 388 566 296 560 C 264 556 242 542 232 520 Z","#3fae6a")+
coloringPart("path","ladybird-leg-front-b",'d="M 682 465 C 714 469 737 484 742 506 L 721 509 C 713 488 697 480 674 480 Z"',3)+ coloringGroup("snail",
coloringPart("path","ladybird-body",'d="M 544 431 C 550 365 596 329 649 338 C 701 347 728 392 715 447 C 702 502 652 520 604 500 C 564 484 540 462 544 431 Z"')+ coloringFlat("snail-foot","M 268 528 C 300 508 356 502 404 512 C 424 516 436 524 438 534 C 400 550 316 552 268 540 Z","#f2b38f")+
coloringPart("path","ladybird-pronotum",'d="M 642 339 C 679 336 710 355 724 385 C 708 406 682 416 655 407 C 634 395 629 365 642 339 Z"',2)+ coloringFlat("snail-neck","M 404 512 C 420 494 440 486 458 490 L 468 522 C 452 530 430 530 412 524 Z","#f2b38f")+
coloringPart("path","ladybird-wing-left",'d="M 548 411 C 567 370 605 350 642 356 C 639 397 644 449 656 498 C 617 508 569 485 552 452 C 545 438 543 424 548 411 Z"',2)+ coloringCircle("snail-head",470,494,26,"#f2b38f","coloring-contour")+
coloringPart("path","ladybird-wing-right",'d="M 642 356 C 675 357 705 382 715 418 C 720 454 696 489 656 498 C 644 449 639 397 642 356 Z"',3)+ coloringContour("snail-shell","M 300 508 C 292 458 322 420 366 418 C 410 416 440 450 434 494 C 428 528 396 546 358 542 C 324 538 302 528 300 508 Z","#f28c3c")+
coloringDot(684,374,4)+ coloringGroup("snail-detail",
coloringPart("circle","ladybird-spot-left-one",'cx="590" cy="397" r="12"',2)+ coloringFlat("shell-spiral","M 366 512 C 342 508 330 490 336 470 C 342 450 364 440 384 448 C 402 456 408 476 400 492 C 394 504 380 512 366 512 Z","#f7c948")+
coloringPart("circle","ladybird-spot-left-two",'cx="579" cy="447" r="13"',3)+ coloringCircle("shell-core",370,478,12,"#e0554d")+
coloringPart("circle","ladybird-spot-left-three",'cx="623" cy="469" r="12"',3)+ coloringDot(462,486,5)+coloringDot(482,486,5),2)+
coloringPart("circle","ladybird-spot-right-one",'cx="681" cy="411" r="12"',2)+ coloringLine("M 462 470 C 452 442 442 424 428 410 M 486 470 C 496 442 508 424 522 410")+
coloringPart("circle","ladybird-spot-right-two",'cx="686" cy="457" r="12"',3)+ coloringDot(428,410,6)+coloringDot(522,410,6)+
coloringLine("path",'d="M 650 357 C 642 402 645 450 656 498" fill="none"',3) coloringLine("M 464 508 C 472 514 482 514 490 508",3))+
)+ coloringGroup("ladybird",
coloringFlower(167,201,"garden-daisy",.82,3)+ coloringLine("M 596 520 C 576 536 562 548 552 560 M 664 520 C 684 536 698 548 708 560 M 600 500 C 578 502 562 500 548 494 M 660 500 C 682 502 698 500 712 494")+
coloringLine("path",'d="M 167 226 V 365 C 133 336 103 341 89 370 M 167 303 C 196 275 228 281 241 310" fill="none"',2) coloringCircle("ladybird-head",630,470,24,"#26344a","coloring-contour")+
)} coloringContour("ladybird-body","M 560 512 C 560 470 592 442 630 442 C 668 442 700 470 700 512 C 700 550 668 570 630 570 C 592 570 560 550 560 512 Z","#e0554d")+
coloringGroup("ladybird-spots",
coloringCircle("ladybird-spot-a",596,490,15,"#26344a")+
coloringCircle("ladybird-spot-b",664,490,15,"#26344a")+
coloringCircle("ladybird-spot-c",590,536,13,"#26344a")+
coloringCircle("ladybird-spot-d",670,536,13,"#26344a")+
coloringDot(620,462,5)+coloringDot(640,462,5),2)+
coloringLine("M 630 448 L 630 566",2)+
coloringLine("M 618 452 C 612 428 606 414 596 404 M 642 452 C 648 428 654 414 664 404",3))+
coloringGroup("grass-front",
coloringLine("M 60 566 C 68 540 72 526 70 508 M 84 570 C 94 546 98 530 98 514 M 748 560 C 756 534 760 520 758 502",2),2)
);
}
function proWindmill(){return coloringScene( function plateWindmill(){
coloringPart("rect","windmill-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("windmill",
coloringPart("path","polder-field",'d="M 4 416 C 156 383 286 405 407 435 C 539 468 663 439 796 402 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringPart("path","polder-canal",'d="M 4 505 C 165 472 287 491 410 522 C 538 552 661 536 796 497 V 596 H 4 Z"',2)+ coloringFlat("cloud-a",coloringCloudPath(70,64,176,58),"#ffffff")+
coloringFigure("smock-windmill", coloringGroup("cloud-b",coloringFlat("cloud-right",coloringCloudPath(540,44,168,54),"#ffffff"),2)+
coloringPart("path","mill-base",'d="M 305 503 L 345 215 H 483 L 532 503 Z"')+ coloringFlat("far-field","M 4 320 C 150 300 300 314 450 328 C 590 340 700 332 796 314 L 796 396 L 4 396 Z","#3fae6a")+
coloringPart("path","mill-cap",'d="M 329 216 C 345 147 380 112 416 111 C 454 112 491 149 504 216 Z"')+ coloringFlat("canal","M 4 396 L 796 396 L 796 452 L 4 452 Z","#2fb7a8")+
coloringPart("path","mill-door",'d="M 381 404 C 381 377 395 363 414 363 C 433 363 447 377 447 404 V 503 H 381 Z"',2)+ coloringFlat("meadow","M 4 452 L 796 452 L 796 596 L 4 596 Z","#3fae6a")+
coloringPart("path","mill-window-left",'d="M 353 282 H 395 V 333 H 353 Z"',3)+ coloringGroup("windmill",
coloringPart("path","mill-window-right",'d="M 437 282 H 479 V 333 H 437 Z"',3)+ coloringFlat("sail-north","M 388 214 L 412 214 L 412 44 L 388 44 Z","#ffffff")+
coloringPart("path","mill-sail-north",'d="M 405 201 L 382 22 L 413 18 L 432 201 Z"')+ coloringFlat("sail-south","M 388 236 L 412 236 L 412 406 L 388 406 Z","#ffffff")+
coloringPart("path","mill-sail-east",'d="M 431 205 L 648 116 L 660 145 L 438 231 Z"',2)+ coloringFlat("sail-west","M 388 214 L 388 238 L 214 238 L 214 214 Z","#ffffff")+
coloringPart("path","mill-sail-south",'d="M 431 235 L 479 526 L 447 530 L 405 240 Z"',2)+ coloringFlat("sail-east","M 412 214 L 412 238 L 586 238 L 586 214 Z","#ffffff")+
coloringPart("path","mill-sail-west",'d="M 402 229 L 172 346 L 157 318 L 394 204 Z"',3)+ coloringContour("mill-body","M 330 452 L 470 452 L 436 268 L 364 268 Z","#8b5a3c")+
coloringPart("circle","mill-hub",'cx="416" cy="219" r="29"')+ coloringFlat("mill-cap","M 356 268 L 444 268 C 440 238 424 220 400 216 C 376 220 360 238 356 268 Z","#e0554d")+
coloringLine("path",'d="M 394 26 L 420 198 M 644 128 L 438 216 M 465 522 L 418 242 M 170 331 L 394 216 M 328 368 H 500 M 323 409 H 508" fill="none"',3) coloringCircle("mill-hub",400,226,18,"#26344a","coloring-contour")+
)+ coloringGroup("mill-detail",
coloringFigure("polder-house", coloringFlat("mill-door","M 382 452 L 418 452 L 418 386 C 412 378 388 378 382 386 Z","#f28c3c")+
coloringPart("path","polder-house-wall",'d="M 53 376 H 241 V 501 H 53 Z"',2)+ coloringFlat("mill-window-left","M 348 356 L 378 356 L 378 326 L 348 326 Z","#f7c948")+
coloringPart("path","polder-house-roof",'d="M 31 376 L 146 273 L 264 376 Z"',2)+ coloringFlat("mill-window-right","M 422 356 L 452 356 L 452 326 L 422 326 Z","#f7c948")+
coloringPart("path","polder-house-door",'d="M 125 425 H 170 V 501 H 125 Z"',3)+ coloringFlat("mill-balcony","M 336 402 L 464 402 L 464 382 L 336 382 Z","#8b5a3c"),2)+
coloringPart("path","polder-house-window-a",'d="M 76 402 H 113 V 446 H 76 Z"',3)+ coloringLine("M 226 220 L 226 232 M 254 218 L 254 234 M 546 218 L 546 234 M 574 220 L 574 232 M 394 60 L 406 60 M 394 92 L 406 92 M 394 372 L 406 372",2)+
coloringPart("path","polder-house-window-b",'d="M 184 402 H 221 V 446 H 184 Z"',3) coloringLine("M 344 428 L 456 428 M 352 300 L 448 300",3))+
)+ coloringGroup("tulips",
coloringCloud("polder-cloud",608,84,.72,3) coloringFlat("tulip-stem-a","M 122 566 C 118 534 118 508 122 486 L 138 486 C 134 508 134 534 138 566 Z","#3fae6a")+
)} coloringFlat("tulip-cup-a","M 100 486 C 100 462 112 446 130 446 C 148 446 160 462 160 486 C 144 496 116 496 100 486 Z","#e0554d")+
coloringFlat("tulip-stem-b","M 202 570 C 198 538 198 512 202 490 L 218 490 C 214 512 214 538 218 570 Z","#3fae6a")+
coloringFlat("tulip-cup-b","M 180 490 C 180 466 192 450 210 450 C 228 450 240 466 240 490 C 224 500 196 500 180 490 Z","#f7c948")+
coloringGroup("tulips-extra",
coloringFlat("tulip-stem-c","M 596 566 C 592 534 592 508 596 486 L 612 486 C 608 508 608 534 612 566 Z","#3fae6a")+
coloringFlat("tulip-cup-c","M 574 486 C 574 462 586 446 604 446 C 622 446 634 462 634 486 C 618 496 590 496 574 486 Z","#d85aa5")+
coloringFlat("tulip-stem-d","M 678 570 C 674 538 674 512 678 490 L 694 490 C 690 512 690 538 694 570 Z","#3fae6a")+
coloringFlat("tulip-cup-d","M 656 490 C 656 466 668 450 686 450 C 704 450 716 466 716 490 C 700 500 672 500 656 490 Z","#f28c3c"),2)+
coloringLine("M 130 452 L 130 486 M 210 456 L 210 490 M 604 452 L 604 486 M 686 456 L 686 490",3))
);
}
function proDinosaurValley(){return coloringScene( function plateDinosaur(){
coloringPart("rect","dinosaur-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("dinosaur-valley",
coloringPart("path","dinosaur-ground",'d="M 4 434 C 145 397 275 417 399 449 C 535 483 662 451 796 412 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#f2b38f")+
coloringFigure("volcanic-valley", coloringFlat("smoke",coloringCloudPath(590,44,190,62),"#8b5a3c")+
coloringPart("path","volcano",'d="M 34 435 L 153 210 L 286 435 Z"',2)+ coloringContour("volcano","M 552 400 L 654 142 C 666 126 688 126 700 142 L 796 400 Z","#8b5a3c")+
coloringPart("path","volcano-rim",'d="M 116 279 L 153 210 L 193 280 L 174 267 L 154 290 L 137 265 Z"',3)+ coloringFlat("lava","M 654 172 C 666 162 688 162 700 172 L 716 220 C 698 208 658 208 640 220 Z","#e0554d")+
coloringPart("path","volcano-smoke-a",'d="M 126 190 C 102 171 109 140 134 134 C 127 105 151 83 177 94 C 199 81 225 100 222 126 C 241 145 228 174 203 178 C 182 197 151 198 126 190 Z"',3) coloringFlat("far-hills","M 4 344 C 110 290 186 336 264 300 C 340 266 420 320 552 300 L 552 400 L 4 400 Z","#7557c8")+
)+ coloringFlat("valley","M 4 400 L 796 400 L 796 596 L 4 596 Z","#3fae6a")+
coloringFigure("brachiosaurus", coloringGroup("ferns",
[384,456,544,604].map((x,i)=>coloringPart("path","dinosaur-leg-"+i,`d="M ${x} 409 C ${x-3} 449 ${x-5} 499 ${x+2} 535 C ${x+17} 541 ${x+38} 541 ${x+52} 534 L ${x+47} 517 C ${x+39} 476 ${x+42} 438 ${x+35} 400 Z"`,i>1?2:1)).join("")+ coloringFlat("fern-left","M 44 552 C 26 500 46 452 84 424 C 96 464 92 508 70 546 C 64 556 52 558 44 552 Z","#2fb7a8")+
coloringPart("path","dinosaur-silhouette",'d="M 275 382 C 229 357 178 338 119 325 C 166 319 211 321 258 330 C 300 337 339 324 374 294 C 416 258 472 248 526 267 C 552 275 573 287 591 302 C 603 252 613 201 619 153 C 625 106 641 74 668 68 C 697 61 725 82 728 112 C 731 139 711 159 684 159 C 681 215 681 277 694 342 C 707 405 681 453 633 470 C 574 491 499 475 446 446 C 384 413 331 409 275 382 Z"')+ coloringFlat("fern-right","M 750 548 C 732 496 752 448 790 420 C 796 460 794 504 772 542 C 766 552 758 554 750 548 Z","#2fb7a8"))+
coloringPart("path","dinosaur-belly",'d="M 369 330 C 423 299 502 301 555 330 C 593 351 620 383 629 424 C 581 454 516 454 457 426 C 419 407 387 377 369 330 Z"',2)+ coloringGroup("dinosaur",
coloringPart("path","dinosaur-cheek",'d="M 655 101 C 673 82 705 88 714 112 C 709 140 678 151 657 135 Z"',3)+ coloringFlat("dino-tail","M 258 452 C 190 442 120 456 68 492 C 104 518 156 522 208 512 C 236 506 250 480 262 466 Z","#3fae6a")+
coloringDot(696,109,4)+ coloringFlat("dino-far-leg","M 372 496 L 372 562 C 372 576 404 576 404 562 L 404 496 Z","#3fae6a")+
coloringLine("path",'d="M 690 137 C 701 141 711 140 718 134 M 292 350 C 331 361 358 379 382 407 M 433 534 H 467 M 505 534 H 539 M 592 534 H 626 M 650 534 H 684" fill="none"',2) coloringFlat("dino-near-leg","M 274 502 L 274 570 C 274 586 312 586 312 570 L 312 502 Z","#3fae6a")+
)+ coloringContour("dino-body","M 244 456 C 248 398 300 364 366 364 C 436 364 484 398 486 452 C 488 504 436 526 364 526 C 292 526 242 510 244 456 Z","#3fae6a")+
coloringPart("circle","dinosaur-sun",'cx="86" cy="93" r="42"',3) coloringContour("dino-neck","M 436 400 C 444 338 464 280 500 240 C 530 248 544 274 532 304 C 512 348 496 392 492 434 C 468 438 446 428 436 400 Z","#3fae6a")+
)} coloringContour("dino-head","M 486 244 C 502 214 542 206 570 222 C 596 238 598 268 578 286 C 552 296 514 288 490 274 C 482 266 482 252 486 244 Z","#3fae6a")+
coloringGroup("dino-detail",
coloringFlat("dino-belly","M 258 484 C 288 508 342 518 396 514 C 432 512 460 504 476 490 C 462 516 416 526 364 526 C 306 526 266 512 258 484 Z","#f7c948")+
coloringFlat("dino-back-plate-a","M 296 372 C 304 344 322 330 342 334 C 328 350 316 362 310 376 Z","#f28c3c")+
coloringFlat("dino-back-plate-b","M 356 364 C 364 336 382 322 402 326 C 388 342 378 354 372 368 Z","#f28c3c")+
coloringFlat("dino-back-plate-c","M 418 370 C 426 342 442 330 460 336 C 446 350 438 362 432 376 Z","#f28c3c")+
coloringCircle("dino-eye",544,240,11,"#ffffff")+
coloringDot(545,241,5)+
coloringFlat("dino-nostril","M 566 250 C 574 246 582 248 586 254 C 580 260 570 260 564 256 Z","#8b5a3c"),2)+
coloringLine("M 578 274 C 566 280 550 282 536 280 M 500 254 C 508 248 518 244 528 244",2)+
coloringLine("M 272 466 C 306 478 356 484 404 482 M 462 320 C 476 328 488 340 496 356 M 90 492 C 132 500 172 500 208 494",3))+
coloringGroup("eggs",
coloringFlat("egg-a","M 168 566 C 156 546 162 522 180 516 C 198 522 204 546 192 566 Z","#f7c948")+
coloringFlat("egg-b","M 214 570 C 202 552 208 530 224 524 C 240 530 246 552 234 570 Z","#f7c948")+
coloringLine("M 168 548 C 176 542 186 542 194 548 M 212 552 C 220 546 230 546 238 552",3),2)
);
}
function proSpringNest(){return coloringScene( function plateNest(){
coloringPart("rect","spring-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("spring-nest",
coloringPart("path","spring-hill",'d="M 4 440 C 144 401 278 423 400 455 C 535 489 662 455 796 417 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringFigure("nesting-branch", coloringCircle("sun",128,110,50,"#f7c948","coloring-contour")+
coloringPart("path","nest-branch",'d="M 42 468 C 176 422 308 422 436 450 C 552 475 668 461 759 421 L 770 455 C 662 496 546 506 424 482 C 297 456 177 458 52 503 Z"')+ coloringFlat("cloud",coloringCloudPath(500,60,190,60),"#ffffff")+
coloringLeaf("nest-leaf-left",107,442,-28,.86,2)+ coloringFlat("far-meadow","M 4 424 C 160 396 320 412 470 428 C 600 442 700 434 796 414 L 796 596 L 4 596 Z","#3fae6a")+
coloringLeaf("nest-leaf-right",688,448,22,.84,3) coloringGroup("branch",
)+ coloringContour("branch-main","M 4 348 C 130 336 260 348 380 372 C 500 396 620 404 796 392 L 796 428 C 616 440 494 430 372 406 C 254 382 128 370 4 382 Z","#8b5a3c")+
coloringFigure("blackbird", coloringFlat("twig-left","M 168 344 C 156 310 162 280 184 258 C 196 284 196 316 186 344 Z","#8b5a3c")+
coloringPart("path","bird-tail-top",'d="M 332 356 C 279 344 229 355 190 387 C 240 401 291 397 341 378 Z"',2)+ coloringFlat("twig-right","M 604 400 C 616 366 640 344 668 336 C 660 366 642 390 620 404 Z","#8b5a3c")+
coloringPart("path","bird-tail-bottom",'d="M 340 379 C 288 387 244 410 216 446 C 267 444 310 427 354 397 Z"',2)+ coloringGroup("blossoms",
coloringPart("path","bird-silhouette",'d="M 322 373 C 326 315 374 278 437 279 C 465 259 493 234 522 215 C 553 194 594 202 617 230 C 640 258 635 299 607 323 C 590 338 569 344 547 341 C 532 377 502 406 457 418 C 397 433 326 413 322 373 Z"')+ coloringCircle("blossom-a",184,246,22,"#d85aa5")+
coloringPart("path","bird-wing",'d="M 364 339 C 397 302 456 299 493 331 C 487 376 445 406 393 399 C 366 385 355 363 364 339 Z"',2)+ coloringCircle("blossom-b",144,286,17,"#d85aa5")+
coloringPart("path","bird-beak",'d="M 607 248 L 681 275 L 611 294 C 620 277 619 264 607 248 Z"',2)+ coloringCircle("blossom-c",676,326,22,"#d85aa5")+
coloringPart("path","bird-throat",'d="M 536 294 C 563 280 600 286 617 310 C 599 340 559 350 532 330 Z"',3)+ coloringCircle("blossom-d",712,356,17,"#d85aa5")+
coloringDot(582,246,5)+ coloringCircle("blossom-e",240,300,15,"#d85aa5"),2)+
coloringLine("path",'d="M 399 322 C 423 341 442 367 447 399 M 396 414 L 389 459 M 459 416 L 466 459 M 389 459 l -18 10 M 389 459 l 17 9 M 466 459 l -17 9 M 466 459 l 18 10" fill="none"',2) coloringLine("M 60 366 C 120 358 180 360 240 368 M 480 410 C 560 418 640 420 720 414",3))+
)+ coloringGroup("nest",
coloringFigure("woven-nest", coloringContour("nest-bowl","M 262 388 C 272 362 320 346 384 346 C 448 346 494 362 502 388 C 496 442 452 476 382 476 C 312 476 268 442 262 388 Z","#f28c3c")+
coloringPart("path","nest-bowl",'d="M 476 430 C 509 396 626 393 666 426 C 657 489 614 518 568 516 C 520 513 487 484 476 430 Z"',2)+ coloringGroup("nest-eggs",
coloringPart("path","nest-rim",'d="M 469 420 C 509 392 628 390 672 420 C 641 450 505 451 469 420 Z"')+ coloringFlat("egg-left","M 318 388 C 306 366 314 342 334 336 C 354 342 360 366 348 388 C 340 396 326 396 318 388 Z","#2fb7a8")+
coloringPart("path","nest-egg-a",'d="M 521 410 C 514 378 529 356 552 356 C 575 357 585 382 578 413 Z"',2)+ coloringFlat("egg-right","M 420 390 C 408 368 416 344 436 338 C 456 344 462 368 450 390 C 442 398 428 398 420 390 Z","#2fb7a8"),2)+
coloringPart("path","nest-egg-b",'d="M 565 407 C 560 374 577 350 601 352 C 625 355 633 383 623 412 Z"',3)+ coloringLine("M 272 396 C 320 408 444 408 492 396 M 280 420 C 330 434 434 434 484 420",2)+
coloringLine("path",'d="M 492 448 C 536 467 607 469 650 449 M 506 476 C 544 489 597 491 632 477" fill="none"',3) coloringLine("M 300 366 C 340 358 424 358 464 366 M 288 442 C 336 456 428 456 476 442",3))+
) coloringGroup("bird",
)} coloringFlat("bird-tail","M 500 296 C 542 280 584 274 618 280 C 596 306 560 322 522 324 C 508 318 500 308 500 296 Z","#3b7dd8")+
coloringContour("bird-body","M 396 306 C 400 262 434 234 476 234 C 520 234 550 264 552 306 C 554 344 518 366 468 366 C 420 366 394 344 396 306 Z","#3b7dd8")+
function proFireStation(){return coloringScene( coloringCircle("bird-head",406,244,38,"#3b7dd8","coloring-contour")+
coloringPart("rect","fire-paper",'x="4" y="4" width="792" height="592" rx="28"')+ coloringFlat("bird-beak","M 372 246 C 350 238 334 244 328 256 C 340 266 358 268 374 262 Z","#f28c3c")+
coloringPart("path","fire-road",'d="M 4 471 H 796 V 596 H 4 Z"')+ coloringGroup("bird-detail",
coloringFigure("fire-station-building", coloringFlat("bird-wing","M 424 288 C 456 274 500 276 526 292 C 512 322 472 336 434 328 C 422 318 418 302 424 288 Z","#2fb7a8")+
coloringPart("path","station-wall",'d="M 27 247 H 301 V 481 H 27 Z"',2)+ coloringFlat("bird-breast","M 398 316 C 414 342 444 356 480 356 C 462 366 424 366 406 350 C 398 340 396 328 398 316 Z","#f7c948")+
coloringPart("path","station-roof",'d="M 10 247 L 164 121 L 319 247 Z"')+ coloringCircle("bird-eye",394,234,12,"#ffffff")+
coloringPart("path","station-door-left",'d="M 58 330 H 158 V 481 H 58 Z"',2)+ coloringDot(394,234,6),2)+
coloringPart("path","station-door-right",'d="M 158 330 H 270 V 481 H 158 Z"',2)+ coloringLine("M 340 250 L 372 254",2)+
coloringPart("path","station-emblem",'d="M 121 188 C 121 160 139 142 164 142 C 189 142 207 160 207 188 C 207 216 188 231 164 231 C 140 231 121 216 121 188 Z"',3)+ coloringLine("M 436 300 C 470 292 502 294 522 302 M 412 326 C 442 336 480 340 512 334",3))+
coloringLine("path",'d="M 164 158 V 218 M 136 188 H 192 M 58 376 H 270 M 108 330 V 481 M 214 330 V 481" fill="none"',3) coloringGroup("front-grass",
)+ coloringLine("M 96 566 C 104 540 108 526 106 508 M 120 570 C 130 546 134 530 134 514 M 700 560 C 708 534 712 520 710 502",2),2)
coloringFigure("fire-engine", );
coloringPart("path","engine-chassis",'d="M 259 439 H 735 L 760 489 H 238 Z"')+ }
coloringPart("path","engine-body",'d="M 291 306 H 541 V 441 H 291 Z"')+
coloringPart("path","engine-cab",'d="M 541 326 H 656 C 679 326 697 340 710 362 L 752 439 H 541 Z"',2)+
coloringPart("path","engine-window-side",'d="M 566 347 H 620 V 392 H 566 Z"',2)+
coloringPart("path","engine-window-front",'d="M 633 347 H 660 C 674 347 685 356 693 370 L 707 392 H 633 Z"',3)+
coloringPart("path","engine-equipment-panel",'d="M 322 334 H 476 V 413 H 322 Z"',2)+
coloringPart("path","engine-ladder",'d="M 300 282 L 575 214 L 582 242 L 308 309 Z"',2)+
coloringPart("circle","engine-wheel-left",'cx="355" cy="482" r="48"')+
coloringPart("circle","engine-wheel-right",'cx="661" cy="482" r="48"')+
coloringPart("circle","engine-hub-left",'cx="355" cy="482" r="21"',2)+
coloringPart("circle","engine-hub-right",'cx="661" cy="482" r="21"',2)+
coloringPart("circle","engine-hose-reel",'cx="416" cy="373" r="31"',3)+
coloringLine("path",'d="M 326 274 L 562 216 M 365 257 L 371 294 M 411 246 L 417 283 M 457 235 L 463 272 M 503 224 L 509 261 M 393 373 C 401 349 437 355 438 379 C 436 401 405 405 397 385" fill="none"',3)
)
)}
function proBeeMeadow(){return coloringScene(
coloringPart("rect","bee-paper",'x="4" y="4" width="792" height="592" rx="28"')+
coloringPart("path","bee-meadow",'d="M 4 456 C 145 415 280 438 402 469 C 535 503 663 468 796 431 L 796 596 L 4 596 Z"')+
coloringFigure("western-honeybee",
coloringPart("path","bee-hindwing",'d="M 337 258 C 283 225 230 231 207 269 C 225 310 281 324 340 295 Z"',2)+
coloringPart("path","bee-forewing",'d="M 367 260 C 330 199 344 139 390 127 C 435 152 439 218 399 272 Z"')+
coloringPart("path","bee-abdomen",'d="M 270 326 C 282 273 333 250 390 268 C 445 285 478 327 466 371 C 454 417 403 438 349 419 C 296 400 259 368 270 326 Z"')+
coloringPart("path","bee-stripe-one",'d="M 319 276 C 342 267 369 267 393 275 C 377 311 375 365 391 419 C 366 424 344 420 325 412 C 308 366 307 317 319 276 Z"',2)+
coloringPart("path","bee-stripe-two",'d="M 416 287 C 439 302 456 322 466 345 C 449 367 439 389 437 407 C 419 418 402 421 384 419 C 404 370 405 320 416 287 Z"',3)+
coloringPart("path","bee-thorax",'d="M 437 289 C 454 257 489 246 519 262 C 548 278 558 315 542 345 C 525 376 488 387 458 369 C 430 351 420 319 437 289 Z"')+
coloringPart("path","bee-head",'d="M 513 268 C 533 232 575 219 607 241 C 639 263 645 307 622 339 C 599 371 555 376 527 350 C 504 328 499 293 513 268 Z"')+
coloringPart("path","bee-compound-eye",'d="M 566 244 C 590 237 613 253 615 276 C 611 299 586 307 566 295 C 551 282 551 259 566 244 Z"',2)+
coloringPart("path","bee-mandible",'d="M 607 303 C 628 301 644 313 645 327 C 633 341 614 344 598 334 Z"',3)+
coloringLine("path",'d="M 557 239 C 541 210 514 201 491 210 M 596 229 C 590 196 605 177 630 170 M 340 292 C 326 330 329 374 347 414 M 397 276 C 383 321 387 378 407 414 M 461 369 L 448 414 M 481 376 L 478 424 M 519 364 L 531 411 M 544 345 L 566 386" fill="none"',2)+
coloringLine("path",'d="M 230 272 C 267 267 305 276 337 293 M 365 251 C 387 216 400 175 390 127" fill="none"',3)
)+
coloringFigure("bee-flowers",
coloringFlower(121,439,"bee-flower-a",.74,2)+
coloringFlower(677,449,"bee-flower-b",.74,3)+
coloringLine("path",'d="M 121 463 V 548 M 677 473 V 548 M 121 512 C 96 492 78 501 72 520 M 677 514 C 704 493 722 503 727 522" fill="none"',2)
)
)}

View file

@ -1,181 +1,238 @@
/* teach — nieuwe kleurboekatlas voor groep 5-6 */ /* teach kleurplaten voor groep 5-6
Rijkere composities met voor-, midden- en achtergrond, en duidelijk meer
vakken op tier 2. Alles van achter naar voren getekend. */
"use strict"; "use strict";
function proRocket(){return coloringScene( function plateRocket(){
coloringPart("rect","rocket-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("rocket",
coloringFigure("orbital-rocket", coloringBackdrop("space","#26344a")+
coloringPart("path","rocket-exhaust-outer",'d="M 347 457 C 348 511 369 554 400 580 C 431 554 452 511 453 457 Z"')+ coloringGroup("stars",
coloringPart("path","rocket-exhaust-inner",'d="M 377 460 C 378 500 387 529 400 544 C 413 529 422 500 423 460 Z"',2)+ coloringFlat("star-a",coloringStarPath(126,108,34),"#f7c948")+
coloringPart("path","rocket-fin-left",'d="M 348 349 C 306 371 282 417 277 475 C 305 458 332 448 357 447 Z"',2)+ coloringFlat("star-b",coloringStarPath(690,486,28),"#f7c948")+
coloringPart("path","rocket-fin-right",'d="M 452 349 C 494 371 518 417 523 475 C 495 458 468 448 443 447 Z"',2)+ coloringGroup("stars-extra",
coloringPart("path","rocket-body",'d="M 349 188 C 354 128 374 74 400 39 C 426 74 446 128 451 188 L 447 437 C 425 463 375 463 353 437 Z"')+ coloringFlat("star-c",coloringStarPath(238,510,22),"#f7c948")+
coloringPart("path","rocket-nose",'d="M 349 188 C 354 128 374 74 400 39 C 426 74 446 128 451 188 C 422 170 378 170 349 188 Z"',2)+ coloringFlat("star-d",coloringStarPath(596,120,20),"#f7c948")+
coloringPart("circle","rocket-window",'cx="400" cy="252" r="48"',2)+ coloringFlat("star-e",coloringStarPath(96,346,17),"#f7c948"),2)+
coloringPart("circle","rocket-window-glass",'cx="400" cy="252" r="31"',3)+ coloringLine("M 320 96 L 336 112 M 336 96 L 320 112 M 468 546 L 484 562 M 484 546 L 468 562 M 176 240 L 190 254 M 190 240 L 176 254",3))+
coloringPart("path","rocket-band",'d="M 351 337 C 378 352 423 352 449 337 L 448 386 C 422 401 378 401 352 386 Z"',2)+ coloringGroup("planet",
coloringPart("path","rocket-nozzle",'d="M 365 435 H 435 L 451 470 H 349 Z"',3)+ coloringFlat("planet-ring-back","M 552 246 C 610 226 692 232 740 258 C 764 272 768 290 752 302 C 716 284 648 276 590 288 C 562 294 548 282 552 246 Z","#7557c8")+
coloringLine("path",'d="M 365 188 C 384 177 416 177 435 188 M 352 386 C 378 399 422 399 448 386" fill="none"',3) coloringCircle("planet",648,300,86,"#f28c3c","coloring-contour")+
)+ coloringFlat("planet-ring-front","M 556 320 C 596 348 690 356 748 336 C 768 328 774 312 760 300 C 720 326 636 332 578 320 C 564 316 556 314 556 320 Z","#7557c8")+
coloringFigure("rocket-space", coloringGroup("planet-detail",
coloringPart("circle","rocket-planet",'cx="648" cy="151" r="68"',2)+ coloringCircle("crater-a",614,272,20,"#f2b38f")+
coloringPart("path","rocket-planet-ring",'d="M 552 159 C 583 113 698 95 748 135 C 718 183 602 199 552 159 Z"',3)+ coloringCircle("crater-b",672,332,16,"#f2b38f")+
coloringPart("path","rocket-moon",'d="M 152 398 C 111 375 105 322 135 291 C 154 271 179 268 199 276 C 170 295 160 328 174 356 C 188 384 218 398 246 389 C 224 422 181 430 152 398 Z"',2)+ coloringCircle("crater-c",690,262,12,"#f2b38f"),2))+
coloringStars([[117,111,13],[198,205,9],[628,328,12],[707,425,11],[106,486,8]],"rocket-star",3) coloringGroup("moon",
) coloringCircle("moon",132,466,60,"#f2b38f","coloring-contour")+
)} coloringGroup("moon-detail",
coloringCircle("moon-crater-a",112,446,17,"#8b5a3c")+
coloringCircle("moon-crater-b",152,486,13,"#8b5a3c")+
coloringCircle("moon-crater-c",106,494,9,"#8b5a3c"),2))+
coloringGroup("rocket",
coloringFlat("fin-left","M 316 386 C 288 404 272 436 270 470 L 330 452 Z","#e0554d")+
coloringFlat("fin-right","M 432 386 C 460 404 476 436 478 470 L 418 452 Z","#e0554d")+
coloringFlat("flame-outer","M 340 470 C 352 522 366 556 374 578 C 382 556 396 522 408 470 Z","#f28c3c")+
coloringContour("rocket-body","M 374 106 C 410 148 432 208 432 274 L 432 470 L 316 470 L 316 274 C 316 208 338 148 374 106 Z","#ffffff")+
coloringFlat("rocket-nose","M 374 106 C 396 132 412 166 422 204 L 326 204 C 336 166 352 132 374 106 Z","#e0554d")+
coloringGroup("rocket-detail",
coloringCircle("porthole-ring",374,282,42,"#3b7dd8")+
coloringCircle("porthole-glass",374,282,28,"#2fb7a8")+
coloringFlat("rocket-band","M 316 348 L 432 348 L 432 380 L 316 380 Z","#3b7dd8")+
coloringFlat("rocket-hatch","M 344 404 L 404 404 L 404 448 L 344 448 Z","#f7c948")+
coloringFlat("flame-inner","M 356 470 C 362 508 370 538 374 556 C 378 538 386 508 392 470 Z","#f7c948"),2)+
coloringLine("M 326 226 L 422 226 M 330 250 L 418 250",2)+
coloringLine("M 352 420 L 396 420 M 352 432 L 396 432 M 288 440 L 316 432 M 460 440 L 432 432",3))
);
}
function proRainforest(){return coloringScene( function plateRainforest(){
coloringPart("rect","rainforest-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("rainforest",
coloringFigure("rainforest-canopy", coloringBackdrop("sky","#2fb7a8")+
coloringPart("path","forest-trunk-left",'d="M 28 596 C 55 497 67 402 62 306 C 58 213 42 127 17 47 C 60 80 89 126 103 185 C 128 126 164 89 220 67 C 180 128 158 194 158 271 C 161 385 184 492 227 596 Z"')+ coloringFlat("canopy-far","M 4 4 C 90 60 150 24 232 66 C 310 106 372 60 452 92 C 530 122 596 78 676 108 C 726 126 766 112 796 88 L 796 4 Z","#3fae6a")+
coloringPart("path","forest-trunk-right",'d="M 588 596 C 621 501 638 399 632 298 C 628 205 610 125 584 59 C 626 85 657 128 674 181 C 700 127 737 91 786 73 L 796 596 Z"',2)+ coloringFlat("forest-floor","M 4 486 C 150 462 320 476 470 490 C 600 502 700 496 796 478 L 796 596 L 4 596 Z","#8b5a3c")+
coloringPart("path","forest-canopy-left",'d="M 4 115 C 17 63 68 39 113 60 C 137 14 198 8 228 52 C 275 22 332 52 335 103 C 365 128 350 172 310 181 H 59 C 21 177 -1 150 4 115 Z"',2)+ coloringGroup("trunks",
coloringPart("path","forest-canopy-right",'d="M 488 110 C 501 60 551 38 596 58 C 622 13 682 11 712 54 C 752 35 790 58 796 93 V 181 H 542 C 503 178 482 149 488 110 Z"',3) coloringContour("trunk-left","M 92 486 C 100 400 104 300 100 190 L 176 190 C 172 300 176 400 184 486 Z","#8b5a3c")+
)+ coloringFlat("trunk-right","M 660 478 C 666 404 668 322 666 246 L 722 246 C 720 322 722 404 728 478 Z","#8b5a3c")+
coloringFigure("scarlet-macaw", coloringGroup("trunk-detail",
coloringPart("path","macaw-tail-long",'d="M 363 389 C 347 454 351 520 379 575 C 406 524 420 461 414 398 Z"')+ coloringLine("M 118 200 C 114 292 114 396 122 480 M 152 200 C 148 292 148 396 156 480 M 686 254 C 682 336 682 410 688 472",2)))+
coloringPart("path","macaw-tail-short",'d="M 405 396 C 409 459 432 513 467 553 C 481 494 466 436 443 384 Z"',2)+ coloringGroup("leaves-back",
coloringPart("path","macaw-silhouette",'d="M 339 348 C 340 286 374 239 425 221 C 435 180 466 151 507 147 C 552 142 591 169 603 209 C 616 252 592 298 551 314 C 535 320 518 321 501 316 C 489 356 459 389 416 403 C 374 415 340 391 339 348 Z"')+ coloringFlat("leaf-a","M 176 208 C 232 172 306 176 344 208 C 300 246 226 250 176 226 Z","#3fae6a")+
coloringPart("path","macaw-wing",'d="M 361 302 C 388 257 439 241 480 262 C 486 316 458 373 413 398 C 377 384 355 347 361 302 Z"',2)+ coloringFlat("leaf-b","M 620 264 C 566 226 494 228 456 258 C 500 298 574 302 622 280 Z","#3fae6a")+
coloringPart("path","macaw-face-patch",'d="M 507 164 C 544 154 579 177 588 211 C 590 244 567 277 531 284 C 506 273 492 247 493 219 C 495 194 499 177 507 164 Z"',3)+ coloringFlat("leaf-c","M 176 314 C 226 282 292 286 326 314 C 286 348 220 352 176 330 Z","#3fae6a")+
coloringPart("path","macaw-upper-beak",'d="M 579 190 C 620 184 651 205 652 237 C 634 258 607 269 581 267 C 594 237 593 214 579 190 Z"',2)+ coloringGroup("leaves-extra",
coloringPart("path","macaw-lower-beak",'d="M 582 267 C 605 263 626 266 640 278 C 626 300 600 306 578 292 Z"',3)+ coloringFlat("leaf-d","M 620 372 C 572 340 508 344 476 372 C 514 404 578 408 620 388 Z","#3fae6a")+
coloringDot(548,205,5)+ coloringFlat("leaf-e","M 176 414 C 220 384 280 388 310 414 C 274 446 214 450 176 428 Z","#3fae6a"),2)+
coloringLine("path",'d="M 388 288 C 418 312 440 347 443 380 M 408 266 C 441 289 460 320 463 352 M 402 401 L 394 446 M 451 391 L 458 443 M 394 446 l -18 10 M 394 446 l 18 9 M 458 443 l -17 10 M 458 443 l 19 8" fill="none"',2) coloringLine("M 186 216 C 240 212 296 214 336 220 M 610 272 C 556 268 500 268 462 272 M 186 322 C 232 318 284 320 318 324",3))+
)+ coloringGroup("branch",
coloringFigure("macaw-perch", coloringContour("branch","M 184 388 C 280 372 400 372 520 388 L 520 420 C 400 404 282 404 184 420 Z","#8b5a3c")+
coloringPart("path","macaw-branch",'d="M 197 445 C 323 410 443 416 557 447 C 639 470 708 461 769 426 L 779 464 C 708 498 632 506 546 483 C 429 452 318 451 207 483 Z"')+ coloringGroup("branch-moss",
coloringLeaf("macaw-leaf-a",257,422,-24,.92,2)+coloringLeaf("macaw-leaf-b",696,450,26,.9,3) coloringFlat("moss-a","M 250 388 C 274 372 306 374 322 388 C 300 398 272 398 250 388 Z","#3fae6a")+
) coloringFlat("moss-b","M 400 386 C 424 370 456 372 472 386 C 450 396 422 396 400 386 Z","#3fae6a"),2))+
)} coloringGroup("macaw",
coloringFlat("macaw-tail-long","M 396 320 C 456 352 512 404 546 466 C 522 476 494 466 470 440 C 436 404 408 364 390 336 Z","#3b7dd8")+
coloringFlat("macaw-tail-short","M 384 322 C 420 356 456 402 478 452 C 456 458 434 446 416 422 C 396 396 382 360 376 336 Z","#7557c8")+
coloringLine("M 330 368 L 330 394 M 330 394 L 314 406 M 330 394 L 346 406")+
coloringContour("macaw-body","M 288 268 C 292 218 326 188 366 188 C 408 188 436 220 436 268 C 436 322 404 380 344 380 C 302 380 286 320 288 268 Z","#e0554d")+
coloringCircle("macaw-head",320,196,48,"#e0554d","coloring-contour")+
coloringFlat("macaw-beak","M 282 190 C 258 178 238 186 232 204 C 240 226 262 236 284 230 C 292 216 292 200 282 190 Z","#26344a")+
coloringGroup("macaw-detail",
coloringFlat("macaw-wing","M 348 244 C 388 232 424 244 438 274 C 428 320 396 356 356 358 C 336 330 336 282 348 244 Z","#f7c948")+
coloringFlat("macaw-belly","M 300 300 C 316 344 344 368 380 372 C 356 382 316 378 300 356 C 292 340 294 318 300 300 Z","#f28c3c")+
coloringCircle("macaw-face-patch",300,190,26,"#ffffff")+
coloringCircle("macaw-eye",300,190,12,"#ffffff")+
coloringDot(300,190,6),2)+
coloringLine("M 252 206 C 262 212 274 214 284 212",2)+
coloringLine("M 356 262 C 392 254 420 262 434 282 M 360 296 C 396 290 422 298 436 316 M 366 330 C 396 326 418 332 430 346",3))+
coloringGroup("flowers",
coloringContour("jungle-flower-a",coloringPetalPath(596,516,-90,54,32),"#d85aa5")+
coloringContour("jungle-flower-b",coloringPetalPath(596,516,-18,52,30),"#d85aa5")+
coloringContour("jungle-flower-c",coloringPetalPath(596,516,54,50,30),"#d85aa5")+
coloringContour("jungle-flower-d",coloringPetalPath(596,516,126,50,30),"#d85aa5")+
coloringContour("jungle-flower-e",coloringPetalPath(596,516,198,52,30),"#d85aa5")+
coloringCircle("jungle-flower-heart",596,516,19,"#f7c948","coloring-contour"))+
coloringGroup("ferns",
coloringFlat("fern-front-left","M 42 566 C 24 514 44 464 82 436 C 94 476 90 520 68 560 C 62 570 50 572 42 566 Z","#3fae6a")+
coloringGroup("fern-extra",
coloringFlat("fern-front-right","M 750 560 C 732 508 752 460 790 432 C 796 472 794 514 772 554 C 766 564 758 566 750 560 Z","#3fae6a"),2))
);
}
function proCastle(){return coloringScene( function plateCastle(){
coloringPart("rect","castle-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("castle",
coloringPart("path","castle-hill",'d="M 4 466 C 147 425 278 448 401 478 C 536 511 663 480 796 440 L 796 596 L 4 596 Z"')+ coloringBackdrop("sky","#3b7dd8")+
coloringFigure("stone-castle", coloringFlat("cloud",coloringCloudPath(72,58,168,54),"#ffffff")+
coloringPart("path","castle-keep",'d="M 239 225 H 561 V 493 H 239 Z"')+ coloringFlat("far-hills","M 4 296 C 116 240 208 292 300 258 C 386 226 456 274 540 252 C 622 230 700 272 796 250 L 796 340 L 4 340 Z","#7557c8")+
coloringPart("path","castle-tower-left",'d="M 58 182 H 239 V 493 H 58 Z"',2)+ coloringFlat("meadow","M 4 340 L 796 340 L 796 468 L 4 468 Z","#3fae6a")+
coloringPart("path","castle-tower-right",'d="M 561 182 H 742 V 493 H 561 Z"',2)+ coloringFlat("river","M 4 468 C 160 452 340 464 490 480 C 610 492 700 488 796 472 L 796 596 L 4 596 Z","#2fb7a8")+
coloringPart("path","castle-roof-left",'d="M 35 182 L 148 43 L 262 182 Z"')+ coloringGroup("castle",
coloringPart("path","castle-roof-right",'d="M 538 182 L 651 43 L 765 182 Z"')+ coloringContour("keep","M 300 440 L 300 232 L 500 232 L 500 440 Z","#f2b38f")+
coloringPart("path","castle-gate",'d="M 332 374 C 332 314 360 282 400 282 C 440 282 468 314 468 374 V 493 H 332 Z"')+ coloringFlat("keep-battlement","M 300 232 L 300 200 L 328 200 L 328 216 L 356 216 L 356 200 L 384 200 L 384 216 L 416 216 L 416 200 L 444 200 L 444 216 L 472 216 L 472 200 L 500 200 L 500 232 Z","#f2b38f")+
coloringPart("path","castle-gate-left",'d="M 344 376 C 344 326 365 300 400 298 V 493 H 344 Z"',2)+ coloringContour("tower-left","M 200 440 L 200 196 L 292 196 L 292 440 Z","#f2b38f")+
coloringPart("path","castle-gate-right",'d="M 400 298 C 435 300 456 326 456 376 V 493 H 400 Z"',2)+ coloringFlat("tower-left-roof","M 246 106 L 312 196 L 180 196 Z","#e0554d")+
[100,175,603,678].map((x,i)=>coloringPart("path","castle-window-"+i,`d="M ${x} 265 C ${x} 235 ${x+15} 218 ${x+31} 218 C ${x+47} 218 ${x+62} 235 ${x+62} 265 V 313 H ${x} Z"`,i>1?3:2)).join("")+ coloringContour("tower-right","M 508 440 L 508 196 L 600 196 L 600 440 Z","#f2b38f")+
coloringPart("path","castle-banner",'d="M 361 185 H 439 V 263 L 400 240 L 361 263 Z"',3)+ coloringFlat("tower-right-roof","M 554 106 L 620 196 L 488 196 Z","#e0554d")+
coloringLine("path",'d="M 239 272 H 561 M 239 351 H 332 M 468 351 H 561 M 91 355 H 206 M 594 355 H 709 M 400 298 V 493 M 354 400 H 446" fill="none"',3) coloringFlat("gate","M 356 440 L 356 340 C 366 314 434 314 444 340 L 444 440 Z","#8b5a3c")+
)+ coloringGroup("castle-detail",
coloringPart("path","castle-road",'d="M 332 493 H 468 L 548 596 H 252 Z"',3) coloringFlat("window-left-a","M 226 260 L 266 260 L 266 306 C 258 292 234 292 226 306 Z","#f7c948")+
)} coloringFlat("window-right-a","M 534 260 L 574 260 L 574 306 C 566 292 542 292 534 306 Z","#f7c948")+
coloringFlat("window-keep-left","M 330 272 L 364 272 L 364 316 C 356 302 338 302 330 316 Z","#f7c948")+
coloringFlat("window-keep-right","M 436 272 L 470 272 L 470 316 C 462 302 444 302 436 316 Z","#f7c948")+
coloringFlat("flag-left","M 246 106 L 246 56 L 316 74 L 246 92 Z","#d85aa5")+
coloringFlat("flag-right","M 554 106 L 554 56 L 624 74 L 554 92 Z","#d85aa5")+
coloringFlat("gate-arch","M 372 440 L 372 348 C 380 328 420 328 428 348 L 428 440 Z","#26344a"),2)+
coloringLine("M 300 380 L 500 380 M 200 340 L 292 340 M 508 340 L 600 340",2)+
coloringLine("M 314 412 L 486 412 M 212 300 L 280 300 M 520 300 L 588 300 M 396 356 L 396 440 M 412 356 L 412 440",3))+
coloringGroup("bridge",
coloringContour("bridge-deck","M 300 468 L 500 468 L 500 500 L 300 500 Z","#8b5a3c")+
coloringFlat("bridge-arch","M 344 500 C 356 528 444 528 456 500 C 440 540 360 540 344 500 Z","#8b5a3c")+
coloringGroup("bridge-detail",
coloringLine("M 330 468 L 330 500 M 366 468 L 366 500 M 434 468 L 434 500 M 470 468 L 470 500",2)))+
coloringGroup("trees",
coloringFlat("tree-left-trunk","M 104 468 C 108 436 110 408 108 380 L 140 380 C 138 408 140 436 144 468 Z","#8b5a3c")+
coloringContour("tree-left-crown","M 60 380 C 32 358 40 314 76 304 C 82 270 126 258 152 282 C 186 288 196 332 168 356 L 140 380 Z","#3fae6a")+
coloringGroup("tree-right",
coloringFlat("tree-right-trunk","M 664 468 C 668 436 670 408 668 380 L 700 380 C 698 408 700 436 704 468 Z","#8b5a3c")+
coloringContour("tree-right-crown","M 620 380 C 592 358 600 314 636 304 C 642 270 686 258 712 282 C 746 288 756 332 728 356 L 700 380 Z","#3fae6a"),2))+
coloringGroup("boat",
coloringContour("boat-hull","M 232 550 C 268 540 344 540 380 550 C 368 574 250 574 232 550 Z","#e0554d")+
coloringFlat("boat-sail","M 302 536 L 302 462 L 366 536 Z","#ffffff")+
coloringGroup("boat-detail",coloringLine("M 306 470 L 306 536 M 320 502 L 348 536",2),2))
);
}
function proCoralReef(){return coloringScene( function plateReef(){
coloringPart("rect","reef-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("coral-reef",
coloringPart("path","reef-sand",'d="M 4 469 C 139 430 269 447 397 477 C 531 509 661 487 796 445 L 796 596 L 4 596 Z"')+ coloringBackdrop("water","#3b7dd8")+
coloringFigure("ocellaris-clownfish", coloringFlat("mid-water","M 4 236 C 150 210 320 226 470 244 C 600 258 700 250 796 230 L 796 452 L 4 452 Z","#2fb7a8")+
coloringPart("path","clownfish-tail",'d="M 223 276 C 178 246 139 211 104 195 C 116 236 117 293 106 335 C 143 322 183 299 223 285 Z"')+ coloringFlat("reef-floor","M 4 452 C 150 428 320 444 470 460 C 600 474 700 466 796 446 L 796 596 L 4 596 Z","#f2b38f")+
coloringPart("path","clownfish-dorsal-fin",'d="M 293 206 C 332 161 385 158 422 196 C 374 193 335 205 303 230 Z"',2)+ coloringGroup("corals",
coloringPart("path","clownfish-ventral-fin",'d="M 323 352 C 357 384 404 383 430 349 C 392 357 357 359 323 352 Z"',3)+ coloringContour("fan-coral-left","M 76 508 C 46 452 60 384 104 350 C 148 384 160 452 132 508 C 116 520 92 520 76 508 Z","#d85aa5")+
coloringPart("path","clownfish-body",'d="M 207 281 C 257 202 365 179 461 213 C 509 230 546 254 576 284 C 548 325 509 349 457 362 C 364 384 258 359 207 281 Z"')+ coloringContour("tube-coral-right","M 668 512 C 664 466 680 428 712 408 C 744 428 758 466 754 512 C 730 524 692 524 668 512 Z","#f28c3c")+
coloringPart("path","clownfish-head-band",'d="M 473 217 C 447 250 448 315 476 356 C 506 344 532 329 553 309 C 532 269 529 247 545 258 C 524 241 501 227 473 217 Z"',2)+ coloringGroup("coral-detail",
coloringPart("path","clownfish-mid-band",'d="M 333 195 C 311 240 311 316 336 370 C 365 376 391 375 416 369 C 395 315 394 243 415 192 C 389 188 359 189 333 195 Z"',2)+ coloringLine("M 104 356 C 100 404 100 456 106 502 M 82 388 C 88 428 90 466 90 500 M 128 388 C 122 428 120 466 120 500")+
coloringPart("path","clownfish-pectoral-fin",'d="M 472 282 C 441 287 416 309 412 339 C 447 343 476 323 486 297 Z"',3)+ coloringFlat("tube-a","M 682 500 C 680 466 688 440 704 424 C 710 452 710 478 704 502 Z","#f7c948")+
coloringDot(524,267,6)+ coloringFlat("tube-b","M 716 502 C 716 470 722 444 736 428 C 744 456 744 480 738 504 Z","#f7c948"),2))+
coloringLine("path",'d="M 535 309 Q 553 318 568 304 M 244 281 C 274 267 304 267 330 282" fill="none"',3) coloringGroup("anemone",
)+ coloringContour("anemone-base","M 268 566 C 268 534 292 512 324 512 C 356 512 380 534 380 566 Z","#7557c8")+
coloringFigure("long-snouted-seahorse", coloringGroup("anemone-arms",
coloringPart("path","seahorse-fin",'d="M 610 318 C 647 305 675 319 683 347 C 662 373 635 380 604 365 Z"',2)+ coloringFlat("arm-a",coloringPetalPath(324,514,-90,58,20),"#d85aa5")+
coloringPart("path","seahorse-silhouette",'d="M 599 213 C 623 187 662 185 689 208 C 705 221 713 241 710 261 L 766 275 C 749 307 715 314 686 294 C 674 332 655 363 634 388 C 609 417 608 449 629 467 C 651 485 682 471 684 445 C 686 424 666 409 648 419 C 660 392 695 390 713 416 C 738 453 716 506 672 518 C 626 531 577 503 570 460 C 562 416 587 384 604 351 C 621 320 625 288 613 267 C 595 261 580 249 578 232 C 577 222 585 213 599 213 Z"')+ coloringFlat("arm-b",coloringPetalPath(324,514,-126,54,18),"#d85aa5")+
coloringPart("path","seahorse-belly",'d="M 624 286 C 653 290 676 309 680 335 C 670 367 648 391 623 405 C 603 387 599 363 609 341 C 620 319 625 300 624 286 Z"',2)+ coloringFlat("arm-c",coloringPetalPath(324,514,-54,54,18),"#d85aa5")+
coloringPart("path","seahorse-crown",'d="M 610 209 L 602 174 L 626 194 L 641 163 L 653 198 L 684 183 L 675 213 Z"',3)+ coloringFlat("arm-d",coloringPetalPath(324,514,-158,48,17),"#d85aa5")+
coloringDot(666,232,5)+ coloringFlat("arm-e",coloringPetalPath(324,514,-22,48,17),"#d85aa5"),2))+
coloringLine("path",'d="M 685 262 Q 704 271 720 262 M 631 304 C 646 318 654 338 652 360 M 622 326 H 657 M 616 350 H 652 M 610 375 H 640" fill="none"',3) coloringGroup("clownfish",
)+ coloringFlat("clownfish-tail","M 300 296 C 268 264 244 252 222 254 C 230 284 230 312 222 342 C 246 344 274 330 302 304 Z","#f28c3c")+
coloringFigure("branching-coral", coloringFlat("clownfish-dorsal","M 356 244 C 370 210 392 190 420 186 C 416 214 408 236 396 250 Z","#f28c3c")+
coloringPart("path","reef-coral",'d="M 88 552 C 101 504 101 454 91 409 C 114 420 128 447 132 477 C 151 440 174 415 201 403 C 200 439 187 472 165 497 C 196 475 224 468 249 477 C 231 506 204 526 172 536 L 176 558 Z"',2)+ coloringFlat("clownfish-pelvic","M 368 348 C 376 376 392 392 412 398 C 410 374 402 356 390 342 Z","#f28c3c")+
coloringPart("path","reef-anemone-base",'d="M 303 545 C 333 508 432 506 463 543 C 435 570 331 572 303 545 Z"',3) coloringContour("clownfish-body","M 296 298 C 316 246 364 218 420 218 C 480 218 526 250 544 298 C 526 346 480 378 420 378 C 364 378 316 350 296 298 Z","#f28c3c")+
) coloringGroup("clownfish-bands",
)} coloringFlat("band-head","M 456 226 C 480 238 498 262 504 294 C 498 326 480 352 456 368 C 446 340 442 316 442 296 C 442 272 446 250 456 226 Z","#ffffff")+
coloringFlat("band-mid","M 366 232 C 382 224 398 220 414 218 C 406 244 402 270 402 298 C 402 326 406 352 414 378 C 398 376 382 372 366 364 C 374 340 378 318 378 298 C 378 276 374 254 366 232 Z","#ffffff")+
coloringCircle("clownfish-eye",496,278,13,"#ffffff")+
coloringDot(498,278,6),2)+
coloringLine("M 520 250 C 532 270 536 288 536 298 C 536 310 532 328 520 346",2)+
coloringLine("M 318 292 C 336 284 352 280 368 278 M 318 308 C 336 314 352 318 368 320",3))+
coloringGroup("seahorse",
coloringFlat("seahorse-fin","M 620 322 C 648 310 674 314 688 330 C 670 348 642 350 620 340 Z","#f7c948")+
coloringContour("seahorse-body","M 588 200 C 620 196 642 216 644 248 C 646 288 626 326 606 358 C 592 382 592 404 608 420 C 626 438 654 438 672 424 C 664 452 630 464 602 452 C 570 438 558 402 572 368 C 586 334 604 300 604 268 C 604 246 596 232 580 228 C 570 220 574 204 588 200 Z","#f7c948")+
coloringFlat("seahorse-snout","M 580 208 C 556 200 536 206 528 220 C 540 234 562 240 584 234 Z","#f7c948")+
coloringGroup("seahorse-detail",
coloringFlat("seahorse-crown","M 596 194 C 592 170 600 152 616 144 C 620 166 614 184 604 196 Z","#f28c3c")+
coloringCircle("seahorse-eye",596,216,11,"#ffffff")+
coloringDot(596,216,5)+
coloringLine("M 614 250 C 626 254 636 254 644 250 M 604 292 C 616 296 626 296 634 292 M 588 336 C 598 342 608 344 616 342",2)))+
coloringGroup("starfish",
coloringFlat("starfish",coloringStarPath(456,528,50),"#e0554d")+
coloringGroup("starfish-detail",
coloringCircle("starfish-heart",456,528,17,"#f7c948")+
coloringLine("M 456 486 L 456 512 M 490 520 L 470 530 M 476 566 L 464 544 M 436 566 L 448 544 M 422 520 L 442 530",2)))+
coloringGroup("bubbles",
coloringCircle("reef-bubble-a",212,196,18,"#ffffff","coloring-contour")+
coloringCircle("reef-bubble-b",176,150,12,"#ffffff","coloring-contour"),2)
);
}
function proMountainCamp(){return coloringScene( function plateCamp(){
coloringPart("rect","camp-paper",'x="4" y="4" width="792" height="592" rx="28"')+ return coloringScene("mountain-camp",
coloringFigure("alpine-range", coloringBackdrop("sky","#7557c8")+
coloringPart("path","mountain-back",'d="M 4 421 L 195 177 L 371 421 Z"')+ coloringCircle("moon",668,110,52,"#f7c948","coloring-contour")+
coloringPart("path","mountain-front",'d="M 288 421 L 518 132 L 796 421 Z"',2)+ coloringGroup("night-stars",
coloringPart("path","mountain-snow-back",'d="M 143 243 L 195 177 L 250 249 L 222 231 L 195 264 L 168 230 Z"',2)+ coloringFlat("camp-star-a",coloringStarPath(120,96,26),"#f7c948")+
coloringPart("path","mountain-snow-front",'d="M 459 206 L 518 132 L 581 211 L 549 192 L 519 228 L 488 190 Z"',3) coloringGroup("camp-stars-extra",
)+ coloringFlat("camp-star-b",coloringStarPath(268,66,19),"#f7c948")+
coloringPart("path","camp-ground",'d="M 4 421 C 148 390 281 409 400 440 C 536 474 663 442 796 405 L 796 596 L 4 596 Z"')+ coloringFlat("camp-star-c",coloringStarPath(430,120,16),"#f7c948"),2)+
coloringFigure("canvas-tent", coloringLine("M 186 168 L 198 180 M 198 168 L 186 180 M 520 84 L 532 96 M 532 84 L 520 96",3))+
coloringPart("path","tent-body",'d="M 94 533 L 304 311 L 515 533 Z"')+ coloringGroup("mountains",
coloringPart("path","tent-front",'d="M 304 311 L 515 533 H 304 Z"',2)+ coloringContour("mountain-far","M 4 396 L 190 168 L 376 396 Z","#2fb7a8")+
coloringPart("path","tent-door",'d="M 304 393 L 410 533 H 304 Z"',3)+ coloringContour("mountain-near","M 300 396 L 520 132 L 740 396 Z","#3b7dd8")+
coloringLine("path",'d="M 304 311 V 533 M 94 533 L 71 557 M 515 533 L 541 557" fill="none"',2) coloringFlat("snow-far","M 148 220 L 190 168 L 232 220 C 212 210 168 210 148 220 Z","#ffffff")+
)+ coloringFlat("snow-near","M 466 196 L 520 132 L 574 196 C 546 182 494 182 466 196 Z","#ffffff")+
coloringFigure("campfire", coloringGroup("mountain-detail",
coloringPart("path","camp-log-back",'d="M 555 523 L 690 556 L 678 583 L 543 549 Z"',2)+ coloringLine("M 190 176 L 190 380 M 520 142 L 520 372 M 232 232 L 300 340 M 574 208 L 656 340",2)))+
coloringPart("path","camp-log-front",'d="M 684 522 L 548 560 L 559 585 L 697 547 Z"',2)+ coloringFlat("lake","M 4 396 L 796 396 L 796 470 L 4 470 Z","#2fb7a8")+
coloringPart("path","camp-flame-outer",'d="M 620 552 C 580 523 591 479 619 449 C 618 482 632 493 642 468 C 678 503 669 538 641 559 Z"')+ coloringFlat("shore","M 4 470 C 160 456 340 466 490 478 C 610 488 700 484 796 470 L 796 596 L 4 596 Z","#3fae6a")+
coloringPart("path","camp-flame-inner",'d="M 625 548 C 611 529 618 506 634 491 C 650 512 650 535 638 551 Z"',3) coloringGroup("pines",
)+ coloringFlat("pine-left-trunk","M 92 470 L 92 430 L 112 430 L 112 470 Z","#8b5a3c")+
coloringPart("circle","camp-moon",'cx="695" cy="92" r="43"',3)+ coloringContour("pine-left","M 40 434 L 102 300 L 164 434 Z","#3fae6a")+
coloringStars([[82,98,10],[356,99,8],[611,78,9]],"camp-star",3) coloringFlat("pine-right-trunk","M 700 478 L 700 438 L 720 438 L 720 478 Z","#8b5a3c")+
)} coloringContour("pine-right","M 652 442 L 710 320 L 768 442 Z","#3fae6a")+
coloringGroup("pines-extra",
function proRobotWorkshop(){return coloringScene( coloringFlat("pine-mid-trunk","M 196 476 L 196 444 L 212 444 L 212 476 Z","#8b5a3c")+
coloringPart("rect","robot-paper",'x="4" y="4" width="792" height="592" rx="28"')+ coloringContour("pine-mid","M 158 448 L 204 350 L 250 448 Z","#3fae6a")))+
coloringPart("path","workshop-floor",'d="M 4 468 H 796 V 596 H 4 Z"')+ coloringGroup("tent",
coloringFigure("service-robot", coloringContour("tent-body","M 300 566 L 400 396 L 500 566 Z","#e0554d")+
coloringPart("path","robot-leg-left",'d="M 339 441 H 389 L 396 548 C 376 561 344 560 329 545 Z"')+ coloringFlat("tent-door","M 366 566 L 400 466 L 434 566 Z","#f7c948")+
coloringPart("path","robot-leg-right",'d="M 411 441 H 461 L 471 545 C 454 560 423 560 404 548 Z"',2)+ coloringGroup("tent-detail",
coloringPart("path","robot-upper-arm-left",'d="M 305 321 C 267 325 237 349 220 386 L 257 405 C 272 379 290 365 316 361 Z"',2)+ coloringFlat("tent-flap-left","M 366 566 L 400 466 L 386 566 Z","#f28c3c")+
coloringPart("path","robot-forearm-left",'d="M 224 382 C 188 400 163 430 155 466 L 193 478 C 203 449 219 430 246 416 Z"',2)+ coloringFlat("tent-flap-right","M 434 566 L 400 466 L 414 566 Z","#f28c3c")+
coloringPart("circle","robot-hand-left",'cx="163" cy="484" r="37"',2)+ coloringLine("M 312 548 L 488 548",2))+
coloringPart("path","robot-upper-arm-right",'d="M 495 321 C 533 325 563 349 580 386 L 543 405 C 528 379 510 365 484 361 Z"',2)+ coloringLine("M 300 566 L 268 578 M 500 566 L 532 578",3))+
coloringPart("path","robot-forearm-right",'d="M 576 382 C 612 400 637 430 645 466 L 607 478 C 597 449 581 430 554 416 Z"',2)+ coloringGroup("campfire",
coloringPart("circle","robot-hand-right",'cx="637" cy="484" r="37"',2)+ coloringFlat("log-left","M 566 566 L 668 542 L 674 560 L 572 584 Z","#8b5a3c")+
coloringPart("path","robot-body",'d="M 300 304 C 326 285 474 285 500 304 V 445 C 466 468 334 468 300 445 Z"')+ coloringFlat("log-right","M 566 552 L 668 576 L 662 594 L 560 570 Z","#8b5a3c")+
coloringPart("path","robot-chest-screen",'d="M 344 338 H 456 V 403 H 344 Z"',2)+ coloringFlat("fire-outer","M 588 552 C 580 512 600 486 616 462 C 626 490 648 500 652 524 C 656 544 646 558 634 566 C 618 574 598 568 588 552 Z","#f28c3c")+
coloringPart("path","robot-neck",'d="M 374 260 H 426 V 298 H 374 Z"',3)+ coloringGroup("fire-detail",
coloringPart("path","robot-head",'d="M 324 152 C 352 132 448 132 476 152 V 259 C 448 279 352 279 324 259 Z"')+ coloringFlat("fire-inner","M 604 548 C 598 522 610 504 620 488 C 626 508 638 516 640 532 C 642 546 634 556 624 560 C 614 562 606 558 604 548 Z","#f7c948"),2))
coloringPart("path","robot-face",'d="M 347 177 H 453 V 232 H 347 Z"',2)+ );
coloringPart("circle","robot-eye-left",'cx="375" cy="204" r="10"',3)+ }
coloringPart("circle","robot-eye-right",'cx="425" cy="204" r="10"',3)+
coloringPart("circle","robot-button-left",'cx="369" cy="428" r="11"',3)+
coloringPart("circle","robot-button-mid",'cx="400" cy="428" r="11"',3)+
coloringPart("circle","robot-button-right",'cx="431" cy="428" r="11"',3)+
coloringLine("path",'d="M 400 137 V 104 M 400 104 H 437 M 371 219 Q 400 237 429 219 M 361 370 L 389 393 L 420 354 L 444 377" fill="none"',3)
)+
coloringFigure("workshop-gears",
coloringPart("path","gear-left",'d="M 85 148 L 105 128 L 126 139 L 138 116 L 166 123 L 169 96 L 198 96 L 202 123 L 230 116 L 242 140 L 265 128 L 284 149 L 267 169 L 286 188 L 267 211 L 243 200 L 231 225 L 203 216 L 199 245 H 170 L 166 216 L 138 225 L 126 201 L 104 212 L 84 190 L 102 170 Z"',2)+
coloringPart("circle","gear-left-center",'cx="185" cy="170" r="39"',3)+
coloringPart("path","gear-right",'d="M 571 153 L 591 133 L 613 143 L 625 119 L 653 127 L 657 99 H 685 L 689 127 L 716 119 L 730 143 L 752 133 L 771 154 L 754 174 L 773 194 L 754 216 L 731 205 L 718 230 L 690 221 L 686 250 H 657 L 653 221 L 625 230 L 613 206 L 590 217 L 570 195 L 588 175 Z"',3)+
coloringPart("circle","gear-right-center",'cx="671" cy="175" r="36"',3)
)
)}
function proCanalStreet(){return coloringScene(
coloringPart("rect","canal-paper",'x="4" y="4" width="792" height="592" rx="28"')+
coloringFigure("canal-houses",
coloringPart("path","canal-house-left",'d="M 31 223 H 196 V 458 H 31 Z"')+
coloringPart("path","canal-gable-left",'d="M 20 223 L 67 174 L 67 141 L 113 90 L 159 141 L 159 174 L 207 223 Z"')+
coloringPart("path","canal-house-mid",'d="M 196 190 H 404 V 458 H 196 Z"',2)+
coloringPart("path","canal-gable-mid",'d="M 185 190 L 225 150 L 225 104 L 300 104 L 300 150 L 415 190 Z"',2)+
coloringPart("path","canal-house-right",'d="M 404 224 H 597 V 458 H 404 Z"',2)+
coloringPart("path","canal-gable-right",'d="M 393 224 L 452 167 L 452 136 L 500 86 L 548 136 L 548 167 L 608 224 Z"',3)+
coloringPart("path","canal-house-end",'d="M 597 249 H 770 V 458 H 597 Z"',3)+
coloringPart("path","canal-gable-end",'d="M 586 249 L 684 157 L 781 249 Z"',3)+
[69,247,456,640].map((x,i)=>coloringPart("path","canal-door-"+i,`d="M ${x} 362 C ${x} 335 ${x+14} 321 ${x+31} 321 C ${x+48} 321 ${x+62} 335 ${x+62} 362 V 458 H ${x} Z"`,i>1?3:2)).join("")+
coloringLine("path",'d="M 50 259 H 93 V 307 H 50 Z M 133 259 H 176 V 307 H 133 Z M 222 226 H 269 V 277 H 222 Z M 319 226 H 366 V 277 H 319 Z M 222 294 H 269 V 341 H 222 Z M 319 294 H 366 V 341 H 319 Z M 430 263 H 476 V 312 H 430 Z M 522 263 H 568 V 312 H 522 Z M 621 286 H 663 V 332 H 621 Z M 704 286 H 746 V 332 H 704 Z" fill="none"',3)
)+
coloringPart("path","canal-quay",'d="M 4 458 H 796 V 493 H 4 Z"')+
coloringPart("path","canal-water",'d="M 4 493 C 162 477 286 492 403 517 C 536 546 662 531 796 502 V 596 H 4 Z"',2)+
coloringFigure("arched-canal-bridge",
coloringPart("path","canal-bridge",'d="M 491 522 C 548 449 672 435 759 493 L 754 535 C 668 486 558 497 502 559 Z"',2)+
coloringLine("path",'d="M 508 505 V 547 M 556 466 V 513 M 610 449 V 495 M 665 448 V 491 M 717 463 V 504" fill="none"',3)
)+
coloringFigure("canal-boat",
coloringPart("path","canal-boat-hull",'d="M 87 524 H 320 L 287 566 H 113 Z"')+
coloringPart("path","canal-boat-cabin",'d="M 133 486 H 263 L 284 524 H 111 Z"',2)+
coloringPart("path","canal-boat-window",'d="M 151 499 H 244 L 253 518 H 142 Z"',3)
)
)}

View file

@ -1,6 +1,31 @@
/* teach - thematische kleurplaten met kwast en vlakvulling */ /* teach - thematische kleurplaten met kwast en verfemmer
Tekenmodel: één SVG per plaat in de 800x600-ruimte, met een echte
schildersvolgorde - de DOM-volgorde ís de tekenvolgorde. Elk kleurvak wordt
bij het renderen uitgeklapt naar een drieluik:
<path class="coloring-region"> het vak zelf: papierwit of de gekozen kleur
<g class="coloring-wash"> de kwaststreken in dat vak
<path class="coloring-outline"> de contour, dus altijd scherp boven de verf
Omdat een leeg vak papierwit is en niet doorzichtig, dekt een vak dat later
komt de verf én de contour van eerdere vakken af. Daarmee lost dit model twee
dingen in één keer op: verf blijft zichtbaar, en je kijkt niet meer door
figuren heen. Eerdere versies legden er één witte contourlaag met
mix-blend-mode over; die blendde door haar eigen stacking context niet en
verborg alle verf. Er staat nu dus nergens meer een dekkende laag bóven de
verf van hetzelfde vak.
Daarnaast: <g class="coloring-free"> voor vrije streken (bovenaan, alleen op
de hogere niveaus) en <g class="coloring-guide"> voor de kleurwijzer.
Alle aanwijzing gebeurt meetkundig met isPointInFill/isPointInStroke vanaf
het papier-element, dus zonder pointer-events om te zetten en zonder
elementFromPoint. */
"use strict"; "use strict";
const COLORING_SVG_NS="http://www.w3.org/2000/svg";
const COLORING_WASH_ALPHA=.72;
const COLORING_THEME_KEYS=Object.freeze({ const COLORING_THEME_KEYS=Object.freeze({
all:"colorThemeAll",animals:"colorThemeAnimals",seasons:"colorThemeSeasons", all:"colorThemeAll",animals:"colorThemeAnimals",seasons:"colorThemeSeasons",
nature:"colorThemeNature",science:"colorThemeScience",culture:"colorThemeCulture" nature:"colorThemeNature",science:"colorThemeScience",culture:"colorThemeCulture"
@ -9,84 +34,100 @@ const COLORING_COLORS=Object.freeze([
"#26344a","#e0554d","#f28c3c","#f7c948","#3fae6a","#2fb7a8", "#26344a","#e0554d","#f28c3c","#f7c948","#3fae6a","#2fb7a8",
"#3b7dd8","#7557c8","#d85aa5","#8b5a3c","#f2b38f","#ffffff" "#3b7dd8","#7557c8","#d85aa5","#8b5a3c","#f2b38f","#ffffff"
]); ]);
let COLORING_UID=0;
function coloringPart(tag,id,attrs="",level=1){ /* --- tekenprimitieven ---------------------------------------------------
const backdrop=tag==="rect"&&attrs==='x="4" y="4" width="792" height="592" rx="28"'; Een kleurvak is een gesloten vorm zónder transform: hetzelfde pad dient
return `<${tag} class="coloring-region" data-fillable="true" data-part="${id}"${backdrop?' data-role="backdrop"':""}${level>1?` data-level="${level}"`:""} ${attrs}/>`; ook als clip voor de kwast. data-tier hoort op een groep of op een
detaillijn - nooit op een los vak - zodat een rekwisiet altijd als geheel
verdwijnt en de plaat op elk niveau een complete tekening blijft. */
/* De vulling staat als presentatie-attribuut in de vorm zelf, nooit in CSS:
een CSS-regel wint van een attribuut, en dat is precies hoe eerdere versies
de verf onbedoeld wegpoetsten.
Een leeg vak is papierwit, niet doorzichtig. Elk vak staat direct vóór zijn
eigen verflaag, dus vakken die later komen dekken de verf van eerdere vakken
af - zo blijft "binnen de lijnen" ook echt binnen de lijnen. Wat er staat
blijft dus zichtbaar; alleen wat er vóór ligt dekt af. */
const COLORING_PAPER="#ffffff";
function coloringRegion(tag,id,attrs,hint,cls){
return `<${tag} class="coloring-region ${cls}" data-fillable="true" data-part="${id}"${hint?` data-hint="${hint}"`:""} fill="${COLORING_PAPER}" ${attrs}/>`;
} }
function coloringLine(tag,attrs="",level=1){ function coloringContour(id,d,hint){return coloringRegion("path",id,`d="${d}"`,hint,"coloring-contour")}
return `<${tag} class="coloring-detail"${level>1?` data-level="${level}"`:""} ${attrs}/>`; function coloringFlat(id,d,hint){return coloringRegion("path",id,`d="${d}"`,hint,"coloring-flat")}
function coloringCircle(id,cx,cy,r,hint,cls="coloring-flat"){
return coloringRegion("circle",id,`cx="${cx}" cy="${cy}" r="${r}"`,hint,cls);
} }
function coloringScene(content){ function coloringEllipse(id,cx,cy,rx,ry,hint,cls="coloring-flat"){
return `<svg class="coloring-sheet" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> return coloringRegion("ellipse",id,`cx="${cx}" cy="${cy}" rx="${rx}" ry="${ry}"`,hint,cls);
<g class="coloring-svg-lines" fill="#fff" fill-rule="evenodd" clip-rule="evenodd" stroke="#26344a" stroke-width="4.8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="2"> }
${content} function coloringRect(id,x,y,w,h,hint,rx=0,cls="coloring-flat"){
</g> return coloringRegion("rect",id,`x="${x}" y="${y}" width="${w}" height="${h}"${rx?` rx="${rx}"`:""}`,hint,cls);
}
function coloringBackdrop(id,hint){
return coloringRegion("rect",id,'x="4" y="4" width="792" height="592" rx="28"',hint,"coloring-backdrop");
}
function coloringLine(d,tier=1){
return `<path class="coloring-detail"${tier>1?` data-tier="${tier}"`:""} fill="none" d="${d}"/>`;
}
function coloringDot(cx,cy,r=5,tier=1){
return `<circle class="coloring-detail coloring-solid"${tier>1?` data-tier="${tier}"`:""} cx="${cx}" cy="${cy}" r="${r}"/>`;
}
function coloringGroup(id,content,tier=1){
return `<g class="coloring-figure" data-figure="${id}"${tier>1?` data-tier="${tier}"`:""}>${content}</g>`;
}
/* Vormgeneratoren met absolute coördinaten. Bewust géén transform: hetzelfde
pad moet ook als clip voor de kwast kunnen dienen. */
const coloringRound=value=>Math.round(value*10)/10;
function coloringCloudPath(x,y,w,h){
const n=coloringRound;
return `M ${n(x)} ${n(y+h)} C ${n(x-h*.16)} ${n(y+h*.44)} ${n(x+w*.05)} ${n(y+h*.06)} ${n(x+w*.27)} ${n(y+h*.28)}`+
` C ${n(x+w*.33)} ${n(y-h*.2)} ${n(x+w*.67)} ${n(y-h*.2)} ${n(x+w*.73)} ${n(y+h*.28)}`+
` C ${n(x+w*.95)} ${n(y+h*.06)} ${n(x+w+h*.16)} ${n(y+h*.44)} ${n(x+w)} ${n(y+h)}`+
` C ${n(x+w*.72)} ${n(y+h*1.14)} ${n(x+w*.28)} ${n(y+h*1.14)} ${n(x)} ${n(y+h)} Z`;
}
function coloringStarPath(cx,cy,r){
const n=coloringRound,points=[];
for(let step=0;step<10;step++){
const angle=(-90+step*36)*Math.PI/180,radius=step%2?r*.42:r;
points.push(`${n(cx+Math.cos(angle)*radius)} ${n(cy+Math.sin(angle)*radius)}`);
}
return `M ${points.join(" L ")} Z`;
}
function coloringPetalPath(cx,cy,angle,len,wide){
const n=coloringRound,rad=angle*Math.PI/180,ux=Math.cos(rad),uy=Math.sin(rad);
const tipX=cx+ux*len,tipY=cy+uy*len,nx=-uy*wide,ny=ux*wide;
return `M ${n(cx)} ${n(cy)} C ${n(cx+nx+ux*len*.28)} ${n(cy+ny+uy*len*.28)} ${n(tipX+nx*.52)} ${n(tipY+ny*.52)} ${n(tipX)} ${n(tipY)}`+
` C ${n(tipX-nx*.52)} ${n(tipY-ny*.52)} ${n(cx-nx+ux*len*.28)} ${n(cy-ny+uy*len*.28)} ${n(cx)} ${n(cy)} Z`;
}
function coloringScene(id,content){
return `<svg class="coloring-sheet" data-scene="${id}" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
<defs class="coloring-clips"></defs>
<g class="coloring-flats">${content}</g>
</svg>`; </svg>`;
} }
function coloringFigure(id,content,attrs=""){
return `<g class="coloring-figure" data-figure="${id}"${attrs?` ${attrs}`:""}>${content}</g>`;
}
function coloringDot(cx,cy,r=5,level=1){
return coloringLine("circle",`cx="${cx}" cy="${cy}" r="${r}" fill="#26344a" stroke="none"`,level);
}
function coloringCloud(id,x,y,scale=1,level=1){
return coloringPart("path",id,`d="M 0 38 C 2 18 20 6 39 13 C 48 -8 82 -10 94 13 C 118 4 140 18 141 40 C 139 57 125 64 105 64 H 28 C 10 64 -1 55 0 38 Z" transform="translate(${x} ${y}) scale(${scale})"`,level);
}
function coloringLeaf(id,x,y,angle=0,scale=1,level=2){
return coloringPart("path",id,`d="M -27 0 C -12 -24 16 -25 30 0 C 14 24 -12 24 -27 0 Z" transform="translate(${x} ${y}) rotate(${angle}) scale(${scale})"`,level);
}
function coloringFlower(cx,cy,prefix,scale=1,level=2){
const petals=[-90,-18,54,126,198].map((angle,index)=>{
const rad=angle*Math.PI/180,x=cx+Math.cos(rad)*25*scale,y=cy+Math.sin(rad)*25*scale;
return coloringPart("path",`${prefix}-petal-${index}`,`d="M ${cx} ${cy} C ${(cx-17*scale).toFixed(1)} ${(cy-20*scale).toFixed(1)} ${(x-15*scale).toFixed(1)} ${(y-16*scale).toFixed(1)} ${x.toFixed(1)} ${y.toFixed(1)} C ${(x+15*scale).toFixed(1)} ${(y+16*scale).toFixed(1)} ${(cx+17*scale).toFixed(1)} ${(cy+20*scale).toFixed(1)} ${cx} ${cy} Z"`,level);
}).join("");
return petals+coloringPart("circle",`${prefix}-heart`,`cx="${cx}" cy="${cy}" r="${15*scale}"`,level);
}
function coloringPetals(cx,cy,prefix,level=2){
return [0,60,120,180,240,300].map((angle,index)=>{
const rad=angle*Math.PI/180,x=(cx+Math.cos(rad)*27).toFixed(1),y=(cy+Math.sin(rad)*27).toFixed(1);
return coloringPart("circle",`${prefix}-petal-${index}`,`cx="${x}" cy="${y}" r="20"`,level);
}).join("")+coloringPart("circle",`${prefix}-heart`,`cx="${cx}" cy="${cy}" r="17"`,level);
}
function coloringStars(points,prefix,level=2){
return points.map(([x,y,r],index)=>coloringPart("path",`${prefix}-${index}`,
`d="M ${x} ${y-r} L ${x+r*.24} ${y-r*.25} L ${x+r} ${y} L ${x+r*.24} ${y+r*.25} L ${x} ${y+r} L ${x-r*.24} ${y+r*.25} L ${x-r} ${y} L ${x-r*.24} ${y-r*.25} Z"`,level)).join("");
}
function coloringRefined(id,svg){
return svg.replace('class="coloring-sheet"',`class="coloring-sheet" data-scene="${id}"`);
}
const COLORING_PAGES=Object.freeze([ const COLORING_PAGES=Object.freeze([
{id:"butterfly",theme:"animals",years:[1,2],title:{nl:"Blije vlinder",en:"Happy butterfly"},svg:coloringRefined("butterfly",proButterfly())}, {id:"butterfly",theme:"animals",years:[1,2],title:{nl:"Vlinder op de klaproos",en:"Butterfly on the poppy"},svg:plateButterfly()},
{id:"autumn",theme:"seasons",years:[1,2],title:{nl:"Herfstboom",en:"Autumn tree"},svg:coloringRefined("autumn",proAutumn())}, {id:"autumn",theme:"seasons",years:[1,2],title:{nl:"Herfstboom",en:"Autumn tree"},svg:plateAutumn()},
{id:"cake",theme:"culture",years:[1,2],title:{nl:"Feesttaart",en:"Party cake"},svg:coloringRefined("cake",proCake())}, {id:"cake",theme:"culture",years:[1,2],title:{nl:"Feesttaart",en:"Party cake"},svg:plateCake()},
{id:"farmyard",theme:"animals",years:[1,2],title:{nl:"Boerderijvrienden",en:"Farmyard friends"},svg:coloringRefined("farmyard",proFarmyard())}, {id:"forest-picnic",theme:"nature",years:[1,2],title:{nl:"Picknick in het bos",en:"Forest picnic"},svg:platePicnic()},
{id:"rainbow-weather",theme:"seasons",years:[1,2],title:{nl:"Regenboogweer",en:"Rainbow weather"},svg:coloringRefined("rainbow-weather",proRainbowWeather())}, {id:"construction-site",theme:"science",years:[1,2],title:{nl:"Grote graafmachine",en:"Big excavator"},svg:plateExcavator()},
{id:"construction-site",theme:"science",years:[1,2],title:{nl:"Grote graafmachine",en:"Big excavator"},svg:coloringRefined("construction-site",proConstruction())}, {id:"ocean",theme:"animals",years:[3,4],title:{nl:"Vrienden in zee",en:"Ocean friends"},svg:plateOcean()},
{id:"forest-picnic",theme:"nature",years:[1,2],title:{nl:"Picknick in het bos",en:"Forest picnic"},svg:coloringRefined("forest-picnic",proForestPicnic())}, {id:"garden",theme:"nature",years:[3,4],title:{nl:"Kleine tuindieren",en:"Little garden animals"},svg:plateGarden()},
{id:"ocean",theme:"animals",years:[3,4],title:{nl:"Vrienden in zee",en:"Ocean friends"},svg:coloringRefined("ocean",proOcean())}, {id:"windmill",theme:"culture",years:[3,4],title:{nl:"Molen in de polder",en:"Windmill in the polder"},svg:plateWindmill()},
{id:"garden",theme:"nature",years:[3,4],title:{nl:"Kleine tuindieren",en:"Little garden animals"},svg:coloringRefined("garden",proGarden())}, {id:"dinosaur-valley",theme:"science",years:[3,4],title:{nl:"Dinosaurusvallei",en:"Dinosaur valley"},svg:plateDinosaur()},
{id:"windmill",theme:"culture",years:[3,4],title:{nl:"Molen in de polder",en:"Windmill in the polder"},svg:coloringRefined("windmill",proWindmill())}, {id:"spring-nest",theme:"seasons",years:[3,4],title:{nl:"Vogelnest in de lente",en:"Spring bird nest"},svg:plateNest()},
{id:"dinosaur-valley",theme:"science",years:[3,4],title:{nl:"Dinosaurusvallei",en:"Dinosaur valley"},svg:coloringRefined("dinosaur-valley",proDinosaurValley())}, {id:"rocket",theme:"science",years:[5,6],title:{nl:"Raket door de ruimte",en:"Rocket through space"},svg:plateRocket()},
{id:"spring-nest",theme:"seasons",years:[3,4],title:{nl:"Vogelnest in de lente",en:"Spring bird nest"},svg:coloringRefined("spring-nest",proSpringNest())}, {id:"rainforest",theme:"nature",years:[5,6],title:{nl:"Regenwoud",en:"Rainforest"},svg:plateRainforest()},
{id:"fire-station",theme:"culture",years:[3,4],title:{nl:"Brandweerkazerne",en:"Fire station"},svg:coloringRefined("fire-station",proFireStation())}, {id:"castle",theme:"culture",years:[5,6],title:{nl:"Kasteel aan de rivier",en:"Castle by the river"},svg:plateCastle()},
{id:"bee-meadow",theme:"nature",years:[3,4],title:{nl:"Bijen in de bloemenwei",en:"Bees in the meadow"},svg:coloringRefined("bee-meadow",proBeeMeadow())}, {id:"coral-reef",theme:"animals",years:[5,6],title:{nl:"Koraalrif",en:"Coral reef"},svg:plateReef()},
{id:"rocket",theme:"science",years:[5,6],title:{nl:"Raket door de ruimte",en:"Rocket through space"},svg:coloringRefined("rocket",proRocket())}, {id:"mountain-camp",theme:"seasons",years:[5,6],title:{nl:"Bergkamp",en:"Mountain camp"},svg:plateCamp()},
{id:"rainforest",theme:"nature",years:[5,6],title:{nl:"Regenwoud",en:"Rainforest"},svg:coloringRefined("rainforest",proRainforest())}, {id:"four-seasons",theme:"seasons",years:[7,8],title:{nl:"Boom van vier seizoenen",en:"Four seasons tree"},svg:plateSeasons()},
{id:"castle",theme:"culture",years:[5,6],title:{nl:"Kasteel en dorp",en:"Castle and village"},svg:coloringRefined("castle",proCastle())}, {id:"eco-city",theme:"science",years:[7,8],title:{nl:"Duurzame stad",en:"Sustainable city"},svg:plateEcoCity()},
{id:"coral-reef",theme:"animals",years:[5,6],title:{nl:"Koraalrif",en:"Coral reef"},svg:coloringRefined("coral-reef",proCoralReef())}, {id:"mandala",theme:"culture",years:[7,8],title:{nl:"Geometrische mandala",en:"Geometric mandala"},svg:plateMandala()},
{id:"mountain-camp",theme:"seasons",years:[5,6],title:{nl:"Bergkamp",en:"Mountain camp"},svg:coloringRefined("mountain-camp",proMountainCamp())}, {id:"ecosystem",theme:"nature",years:[7,8],title:{nl:"Leven rond de vijver",en:"Life around the pond"},svg:plateEcosystem()},
{id:"robot-workshop",theme:"science",years:[5,6],title:{nl:"Robotwerkplaats",en:"Robot workshop"},svg:coloringRefined("robot-workshop",proRobotWorkshop())}, {id:"savanna-web",theme:"animals",years:[7,8],title:{nl:"Leven op de savanne",en:"Life on the savanna"},svg:plateSavanna()}
{id:"canal-street",theme:"culture",years:[5,6],title:{nl:"Straat langs de gracht",en:"Canal street"},svg:coloringRefined("canal-street",proCanalStreet())},
{id:"four-seasons",theme:"seasons",years:[7,8],title:{nl:"Boom van vier seizoenen",en:"Four seasons tree"},svg:coloringRefined("four-seasons",proFourSeasons())},
{id:"eco-city",theme:"science",years:[7,8],title:{nl:"Duurzame stad",en:"Sustainable city"},svg:coloringRefined("eco-city",proEcoCity())},
{id:"mandala",theme:"culture",years:[7,8],title:{nl:"Geometrische mandala",en:"Geometric mandala"},svg:coloringRefined("mandala",proMandala())},
{id:"ecosystem",theme:"nature",years:[7,8],title:{nl:"Leven rond de vijver",en:"Life around the pond"},svg:coloringRefined("ecosystem",proEcosystem())},
{id:"solar-observatory",theme:"science",years:[7,8],title:{nl:"Sterrenwacht",en:"Solar observatory"},svg:coloringRefined("solar-observatory",proSolarObservatory())},
{id:"savanna-web",theme:"animals",years:[7,8],title:{nl:"Leven op de savanne",en:"Life on the savanna"},svg:coloringRefined("savanna-web",proSavannaWeb())},
{id:"japanese-garden",theme:"culture",years:[7,8],title:{nl:"Japanse tuin",en:"Japanese garden"},svg:coloringRefined("japanese-garden",proJapaneseGarden())}
]); ]);
function coloringPagesFor(yearGroup,theme="all"){ function coloringPagesFor(yearGroup,theme="all"){
@ -100,16 +141,39 @@ function coloringBrushWidth(size,pressure,pointerType){
function coloringSafeColor(value,fallback=COLORING_COLORS[1]){ function coloringSafeColor(value,fallback=COLORING_COLORS[1]){
return /^#[0-9a-f]{6}$/i.test(String(value||""))?String(value).toLowerCase():fallback; return /^#[0-9a-f]{6}$/i.test(String(value||""))?String(value).toLowerCase():fallback;
} }
function coloringInkOverlay(svg){ /* Binnen de lijnen blijven is hulp, niet een beperking: groep 1-4 krijgt het op
const overlay=svg.cloneNode(true);overlay.setAttribute("class","coloring-ink-svg coloring-ink-overlay"); niveau 1 en 2, groep 5-8 alleen op niveau 1. Op het hoogste niveau schildert
overlay.querySelectorAll(".coloring-recognition").forEach(node=>node.remove()); iedereen vrij over de plaat. */
overlay.querySelectorAll("[data-fillable]").forEach(node=>{ function coloringClipsStrokes(yearGroup,level){
node.removeAttribute("data-fillable");node.removeAttribute("data-part");node.setAttribute("fill","#fff"); return Number(level)===1||(Number(yearGroup)<=4&&Number(level)===2);
}); }
return overlay; function coloringVisible(node){return !node.hasAttribute("hidden")&&!node.closest("[hidden]")}
function coloringRegionAt(sheet,point){
const nodes=sheet.querySelectorAll(".coloring-flats [data-part]");
for(let index=nodes.length-1;index>=0;index--){
const node=nodes[index];
if(!coloringVisible(node)||typeof node.isPointInFill!=="function")continue;
try{if(node.isPointInFill(point))return node}catch(_){/* vorm zonder meetkunde slaan we over */}
}
return null;
}
function coloringStrokeAt(sheet,point){
const paths=sheet.querySelectorAll(".coloring-stroke path");
for(let index=paths.length-1;index>=0;index--){
try{if(paths[index].isPointInStroke(point))return paths[index].closest(".coloring-stroke")}catch(_){}
}
return null;
}
/* Wat je bovenop ziet, haalt de gum weg: de laag die het laatst getekend is
wint, dus een streek onder een gevuld vak blijft beschermd. */
function coloringTopLayer(strokeGroup,filledRegion){
if(!strokeGroup)return filledRegion||null;
if(!filledRegion)return strokeGroup;
return strokeGroup.compareDocumentPosition(filledRegion)&Node.DOCUMENT_POSITION_FOLLOWING?filledRegion:strokeGroup;
} }
function mountColoring(root,initState,opts={}){ function mountColoring(root,initState,opts={}){
const uid=++COLORING_UID;
const yearGroup=Math.max(1,Math.min(8,Number(initState&&initState.yearGroup)||4)); const yearGroup=Math.max(1,Math.min(8,Number(initState&&initState.yearGroup)||4));
const level=Math.max(1,Math.min(3,Number(initState&&initState.level)||2)); const level=Math.max(1,Math.min(3,Number(initState&&initState.level)||2));
const savedTool=initState&&initState.tool==="pencil"?"brush":initState&&initState.tool; const savedTool=initState&&initState.tool==="pencil"?"brush":initState&&initState.tool;
@ -118,8 +182,13 @@ function mountColoring(root,initState,opts={}){
let size=[4,7,12].includes(Number(initState&&initState.size))?Number(initState.size):7; let size=[4,7,12].includes(Number(initState&&initState.size))?Number(initState.size):7;
let theme=COLORING_THEME_KEYS[initState&&initState.theme]?initState.theme:"all"; let theme=COLORING_THEME_KEYS[initState&&initState.theme]?initState.theme:"all";
let pageId=String(initState&&initState.pageId||""); let pageId=String(initState&&initState.pageId||"");
let fills=initState&&initState.pageId===pageId&&initState.fills&&typeof initState.fills==="object"?{...initState.fills}:{}; const samePage=!!(initState&&initState.pageId&&String(initState.pageId)===pageId);
let completed=!!(initState&&initState.completed),restoreInk=initState&&initState.ink; let fills=samePage&&initState.fills&&typeof initState.fills==="object"?{...initState.fills}:{};
let strokes=samePage&&Array.isArray(initState.strokes)?initState.strokes.slice():[];
let legacyInk=samePage&&!strokes.length&&typeof initState.ink==="string"?initState.ink:null;
let completed=!!(initState&&initState.completed);
let guide=initState&&typeof initState.guide==="boolean"?initState.guide:level===1;
const clipStrokes=coloringClipsStrokes(yearGroup,level);
root.innerHTML=`<div class="coloring-app"> root.innerHTML=`<div class="coloring-app">
<div class="coloring-choices"> <div class="coloring-choices">
@ -132,29 +201,33 @@ function mountColoring(root,initState,opts={}){
<div class="coloring-palette"></div> <div class="coloring-palette"></div>
<label class="coloring-custom"><span></span><input type="color"></label> <label class="coloring-custom"><span></span><input type="color"></label>
<div class="coloring-sizes"></div> <div class="coloring-sizes"></div>
<button class="coloring-guide-btn" type="button">🎯</button>
<button class="coloring-reset" type="button"></button> <button class="coloring-reset" type="button"></button>
<button class="coloring-done tbtn" type="button"></button> <button class="coloring-done tbtn" type="button"></button>
</div> </div>
<div class="coloring-stage"><div class="coloring-paper" role="img"><div class="coloring-art"></div><canvas width="800" height="600"></canvas><div class="coloring-ink" aria-hidden="true"></div></div></div> <div class="coloring-stage"><div class="coloring-paper" role="img"></div></div>
<div class="coloring-hint" aria-live="polite"></div> <div class="coloring-hint" aria-live="polite"></div>
</div>`; </div>`;
const app=root.querySelector(".coloring-app"),themeSel=root.querySelector(".coloring-theme"),pageSel=root.querySelector(".coloring-page"); const app=root.querySelector(".coloring-app"),themeSel=root.querySelector(".coloring-theme"),pageSel=root.querySelector(".coloring-page");
const art=root.querySelector(".coloring-art"),inkHost=root.querySelector(".coloring-ink"),paper=root.querySelector(".coloring-paper"),canvas=root.querySelector("canvas"),ctx=canvas.getContext("2d"); const paper=root.querySelector(".coloring-paper");
const toolHost=root.querySelector(".coloring-tool-buttons"),palette=root.querySelector(".coloring-palette"),sizes=root.querySelector(".coloring-sizes"); const toolHost=root.querySelector(".coloring-tool-buttons"),palette=root.querySelector(".coloring-palette"),sizes=root.querySelector(".coloring-sizes");
const custom=root.querySelector(".coloring-custom input"),reset=root.querySelector(".coloring-reset"),done=root.querySelector(".coloring-done"),hint=root.querySelector(".coloring-hint"); const custom=root.querySelector(".coloring-custom input"),reset=root.querySelector(".coloring-reset"),done=root.querySelector(".coloring-done");
const guideBtn=root.querySelector(".coloring-guide-btn"),hint=root.querySelector(".coloring-hint");
const TOOLS=[["brush","🖌️","colorToolBrush"],["bucket","🪣","colorToolBucket"],["eraser","🧽","eraser"]]; const TOOLS=[["brush","🖌️","colorToolBrush"],["bucket","🪣","colorToolBucket"],["eraser","🧽","eraser"]];
let sheet=null,flats=null,freeHost=null,clips=null,guideHost=null;
TOOLS.forEach(([id,icon])=>{const button=document.createElement("button"),symbol=document.createElement("span"),label=document.createElement("span");button.type="button";button.dataset.tool=id;symbol.className="coloring-tool-icon";symbol.textContent=icon;symbol.setAttribute("aria-hidden","true");label.className="coloring-tool-label";button.append(symbol,label);button.onclick=()=>{tool=id;refreshTools()};toolHost.appendChild(button)}); TOOLS.forEach(([id,icon])=>{const button=document.createElement("button"),symbol=document.createElement("span"),label=document.createElement("span");button.type="button";button.dataset.tool=id;symbol.className="coloring-tool-icon";symbol.textContent=icon;symbol.setAttribute("aria-hidden","true");label.className="coloring-tool-label";button.append(symbol,label);button.onclick=()=>{tool=id;refreshTools()};toolHost.appendChild(button)});
COLORING_COLORS.forEach((value,index)=>{const button=document.createElement("button");button.type="button";button.className="coloring-color";button.dataset.color=value;button.dataset.index=index+1;const dot=document.createElement("i");dot.style.background=value;button.appendChild(dot);button.onclick=()=>{color=value;custom.value=value;if(tool==="eraser")tool="brush";refreshTools()};palette.appendChild(button)}); COLORING_COLORS.forEach((value,index)=>{const button=document.createElement("button");button.type="button";button.className="coloring-color";button.dataset.color=value;button.dataset.index=index+1;const dot=document.createElement("i");dot.style.background=value;button.appendChild(dot);button.onclick=()=>{color=value;custom.value=value;if(tool==="eraser")tool="brush";refreshTools()};palette.appendChild(button)});
[4,7,12].forEach(value=>{const button=document.createElement("button");button.type="button";button.className="coloring-size";button.dataset.size=value;const dot=document.createElement("i");dot.style.width=dot.style.height=(value+5)+"px";button.appendChild(dot);button.onclick=()=>{size=value;refreshTools()};sizes.appendChild(button)}); [4,7,12].forEach(value=>{const button=document.createElement("button");button.type="button";button.className="coloring-size";button.dataset.size=value;const dot=document.createElement("i");dot.style.width=dot.style.height=(value+5)+"px";button.appendChild(dot);button.onclick=()=>{size=value;refreshTools()};sizes.appendChild(button)});
custom.value=color;custom.oninput=()=>{color=coloringSafeColor(custom.value,color);if(tool==="eraser")tool="brush";refreshTools()}; custom.value=color;custom.oninput=()=>{color=coloringSafeColor(custom.value,color);if(tool==="eraser")tool="brush";refreshTools()};
guideBtn.onclick=()=>{guide=!guide;renderGuide();refreshTools()};
function pageTitle(page){return page.title[LANG]||page.title.nl} function pageTitle(page){return page.title[LANG]||page.title.nl}
function eligibleThemes(){return [...new Set(coloringPagesFor(yearGroup).map(page=>page.theme))]} function eligibleThemes(){return [...new Set(coloringPagesFor(yearGroup).map(page=>page.theme))]}
function currentPages(){return coloringPagesFor(yearGroup,theme)} function currentPages(){return coloringPagesFor(yearGroup,theme)}
function rebuildThemeOptions(){ function rebuildThemeOptions(){
const previous=theme;themeSel.replaceChildren(); const previous=theme;themeSel.replaceChildren();
const all=new Option(T("colorThemeAll"),"all");themeSel.add(all); themeSel.add(new Option(T("colorThemeAll"),"all"));
eligibleThemes().forEach(id=>themeSel.add(new Option(T(COLORING_THEME_KEYS[id]),id))); eligibleThemes().forEach(id=>themeSel.add(new Option(T(COLORING_THEME_KEYS[id]),id)));
theme=themeSel.querySelector(`option[value="${previous}"]`)?previous:"all";themeSel.value=theme; theme=themeSel.querySelector(`option[value="${previous}"]`)?previous:"all";themeSel.value=theme;
} }
@ -163,23 +236,105 @@ function mountColoring(root,initState,opts={}){
pages.forEach(page=>pageSel.add(new Option(pageTitle(page),page.id))); pages.forEach(page=>pageSel.add(new Option(pageTitle(page),page.id)));
pageId=pages.some(page=>page.id===prefer)?prefer:(pages[0]&&pages[0].id)||"";pageSel.value=pageId; pageId=pages.some(page=>page.id===prefer)?prefer:(pages[0]&&pages[0].id)||"";pageSel.value=pageId;
} }
function clearInk(){ctx.clearRect(0,0,canvas.width,canvas.height)} const svgNode=name=>document.createElementNS(COLORING_SVG_NS,name);
function loadInk(url){ function point(event){
clearInk();if(!url)return; const rect=paper.getBoundingClientRect();
const image=new Image();image.onload=()=>{clearInk();ctx.drawImage(image,0,0,canvas.width,canvas.height)};image.src=url; return{x:(event.clientX-rect.left)*800/rect.width,y:(event.clientY-rect.top)*600/rect.height};
}
const domPoint=pt=>new DOMPoint(pt.x,pt.y);
/* Elk vak wordt uitgeklapt naar vak -> verflaag -> contour. De contour is een
afgeleide van het vak zelf, dus er is één bron voor de tekening en de
contour kan per definitie niets afdekken (fill="none"). */
function expandRegions(){
flats.querySelectorAll("[data-fillable][data-part]").forEach(region=>{
const wash=svgNode("g");
wash.setAttribute("class","coloring-wash");wash.dataset.for=region.dataset.part;
const outline=region.cloneNode(false);
outline.setAttribute("class",region.getAttribute("class").replace("coloring-region","coloring-outline"));
["data-fillable","data-part","data-hint","data-hint-at"].forEach(name=>outline.removeAttribute(name));
outline.setAttribute("fill","none");
region.parentNode.insertBefore(wash,region.nextSibling);
region.parentNode.insertBefore(outline,wash.nextSibling);
});
}
function clipFor(region){
const id=`cclip-${uid}-${region.dataset.part}`;
if(!clips.querySelector(`[id="${id}"]`)){
const clip=svgNode("clipPath");clip.id=id;
const shape=region.cloneNode(false);
["class","data-fillable","data-part","data-hint","data-hint-at","fill","hidden"].forEach(name=>shape.removeAttribute(name));
clip.appendChild(shape);clips.appendChild(clip);
}
return `url(#${id})`;
}
function washFor(region){
const next=region.nextElementSibling;
return next&&next.classList.contains("coloring-wash")?next:freeHost;
}
function strokePath(seg,strokeColor){
const path=svgNode("path");
path.setAttribute("fill","none");path.setAttribute("stroke",strokeColor);
path.setAttribute("stroke-width",String(seg.w));
path.setAttribute("stroke-linecap","round");path.setAttribute("stroke-linejoin","round");
path.setAttribute("d",seg.d);return path;
}
function paintStroke(stroke){
const region=stroke.part?flats.querySelector(`[data-part="${stroke.part}"]`):null;
const clipped=region&&coloringVisible(region);
const group=svgNode("g");group.setAttribute("class","coloring-stroke");group.setAttribute("opacity",String(COLORING_WASH_ALPHA));
if(clipped)group.setAttribute("clip-path",clipFor(region));
stroke.segs.forEach(seg=>group.appendChild(strokePath(seg,stroke.color)));
(clipped?washFor(region):freeHost).appendChild(group);
return group;
}
function renderGuide(){
guideHost.replaceChildren();
guideBtn.hidden=level>=3;
if(!guide||level>=3)return;
flats.querySelectorAll("[data-hint]").forEach(region=>{
if(!coloringVisible(region))return;
const filled=fills[region.dataset.part],want=region.dataset.hint;
if(filled&&filled.toLowerCase()===want.toLowerCase())return;
let cx,cy;
const at=region.dataset.hintAt;
if(at){const parts=at.split(/\s+/);cx=Number(parts[0]);cy=Number(parts[1])}
else{const box=region.getBBox();cx=box.x+box.width/2;cy=box.y+box.height/2}
const dot=svgNode("circle");
dot.setAttribute("class","coloring-guide-dot");dot.setAttribute("cx",String(Math.round(cx)));
dot.setAttribute("cy",String(Math.round(cy)));dot.setAttribute("r","13");
dot.setAttribute("fill",want);dot.setAttribute("opacity",filled?".3":"1");
guideHost.appendChild(dot);
});
} }
function renderPage(keepState){ function renderPage(keepState){
const page=COLORING_PAGES.find(item=>item.id===pageId)||currentPages()[0];if(!page)return; const page=COLORING_PAGES.find(item=>item.id===pageId)||currentPages()[0];if(!page)return;
pageId=page.id;art.innerHTML=page.svg; pageId=page.id;paper.innerHTML=page.svg;
const svg=art.querySelector("svg");paper.setAttribute("aria-label",pageTitle(page)); sheet=paper.querySelector("svg");paper.setAttribute("aria-label",pageTitle(page));
svg.querySelectorAll("[data-level]").forEach(node=>node.toggleAttribute("hidden",Number(node.dataset.level)>level)); flats=sheet.querySelector(".coloring-flats");
if(!keepState){fills={};completed=false;restoreInk=null;clearInk()} clips=sheet.querySelector(".coloring-clips");
svg.querySelectorAll("[data-fillable][data-part]").forEach(node=>{ if(!keepState){fills={};strokes=[];legacyInk=null;completed=false}
sheet.querySelectorAll("[data-tier]").forEach(node=>node.toggleAttribute("hidden",Number(node.dataset.tier)>level));
expandRegions();
freeHost=svgNode("g");freeHost.setAttribute("class","coloring-free");flats.appendChild(freeHost);
if(legacyInk){
/* werk van vóór de SVG-verflaag: als losse afbeelding bovenop, zodat oud
gekleurd werk zichtbaar blijft */
const image=svgNode("image");image.setAttribute("class","coloring-legacy-ink");
image.setAttribute("x","0");image.setAttribute("y","0");image.setAttribute("width","800");image.setAttribute("height","600");
image.setAttribute("href",legacyInk);freeHost.appendChild(image);
}
guideHost=svgNode("g");guideHost.setAttribute("class","coloring-guide");
guideHost.setAttribute("pointer-events","none");sheet.appendChild(guideHost);
flats.querySelectorAll("[data-fillable][data-part]").forEach(node=>{
const saved=coloringSafeColor(fills[node.dataset.part],"");if(saved)node.setAttribute("fill",saved); const saved=coloringSafeColor(fills[node.dataset.part],"");if(saved)node.setAttribute("fill",saved);
}); });
inkHost.replaceChildren(coloringInkOverlay(svg)); strokes.forEach(paintStroke);
if(keepState&&restoreInk){loadInk(restoreInk);restoreInk=null} renderGuide();refreshTools();updateTexts();
refreshTools();updateTexts(); }
function levelHint(){return T("colorLevelHelp"+level)}
function toolHint(){
return T(tool==="bucket"?"colorBucketHint":tool==="eraser"?"colorEraserHint":"colorBrushHint")+" "+levelHint();
} }
function updateTexts(){ function updateTexts(){
root.querySelector(".coloring-theme-label").textContent=T("colorTheme"); root.querySelector(".coloring-theme-label").textContent=T("colorTheme");
@ -190,61 +345,123 @@ function mountColoring(root,initState,opts={}){
palette.querySelectorAll("button").forEach(button=>button.setAttribute("aria-label",`${T("penColor")} ${button.dataset.index}`)); palette.querySelectorAll("button").forEach(button=>button.setAttribute("aria-label",`${T("penColor")} ${button.dataset.index}`));
custom.parentElement.querySelector("span").textContent=T("customColor"); custom.parentElement.querySelector("span").textContent=T("customColor");
reset.title=T("colorReset");reset.setAttribute("aria-label",T("colorReset")); reset.title=T("colorReset");reset.setAttribute("aria-label",T("colorReset"));
guideBtn.title=T("colorGuide");guideBtn.setAttribute("aria-label",T("colorGuide"));
done.textContent=completed?"✓ "+T("colorFinished"):T("colorDone"); done.textContent=completed?"✓ "+T("colorFinished"):T("colorDone");
hint.textContent=T(tool==="bucket"?"colorBucketHint":tool==="eraser"?"colorEraserHint":"colorBrushHint"); hint.textContent=toolHint();
const selected=pageId;rebuildPageOptions(selected); rebuildPageOptions(pageId);
} }
function refreshTools(){ function refreshTools(){
toolHost.querySelectorAll("button").forEach(button=>{const on=button.dataset.tool===tool;button.classList.toggle("on",on);button.setAttribute("aria-pressed",String(on))}); toolHost.querySelectorAll("button").forEach(button=>{const on=button.dataset.tool===tool;button.classList.toggle("on",on);button.setAttribute("aria-pressed",String(on))});
palette.querySelectorAll("button").forEach(button=>button.classList.toggle("on",button.dataset.color===color&&tool!=="eraser")); palette.querySelectorAll("button").forEach(button=>button.classList.toggle("on",button.dataset.color===color&&tool!=="eraser"));
sizes.querySelectorAll("button").forEach(button=>{button.classList.toggle("on",Number(button.dataset.size)===size);button.title=T("penSize")}); sizes.querySelectorAll("button").forEach(button=>{button.classList.toggle("on",Number(button.dataset.size)===size);button.title=T("penSize")});
app.dataset.tool=tool;canvas.style.pointerEvents=tool==="bucket"?"none":"auto";art.style.pointerEvents=tool==="bucket"?"auto":"none"; guideBtn.classList.toggle("on",guide&&level<3);guideBtn.setAttribute("aria-pressed",String(guide&&level<3));
hint.textContent=T(tool==="bucket"?"colorBucketHint":tool==="eraser"?"colorEraserHint":"colorBrushHint"); app.dataset.tool=tool;app.dataset.clip=String(clipStrokes);
hint.textContent=toolHint();
} }
themeSel.onchange=()=>{theme=themeSel.value;rebuildPageOptions("");renderPage(false)}; themeSel.onchange=()=>{theme=themeSel.value;rebuildPageOptions("");renderPage(false)};
pageSel.onchange=()=>{pageId=pageSel.value;renderPage(false)}; pageSel.onchange=()=>{pageId=pageSel.value;renderPage(false)};
art.addEventListener("click",event=>{
if(tool!=="bucket")return;const part=event.target.closest("[data-fillable][data-part]");if(!part||part.hasAttribute("hidden"))return;
part.setAttribute("fill",color);fills[part.dataset.part]=color;completed=false;updateTexts();
});
function partBelow(event){ function fillAt(pt){
const canvasEvents=canvas.style.pointerEvents,artEvents=art.style.pointerEvents; const region=coloringRegionAt(sheet,domPoint(pt));if(!region)return false;
let part=null; const part=region.dataset.part;
try{ region.setAttribute("fill",color);fills[part]=color;
canvas.style.pointerEvents="none";art.style.pointerEvents="auto"; /* de emmer dekt het vak volledig: kwastwerk in dit vak verdwijnt eronder */
const target=document.elementFromPoint(event.clientX,event.clientY); completed=false;renderGuide();return true;
part=target&&typeof target.closest==="function"?target.closest("[data-fillable][data-part]"):null; }
}finally{ function eraseAt(pt){
canvas.style.pointerEvents=canvasEvents;art.style.pointerEvents=artEvents; const target=domPoint(pt);
const strokeGroup=coloringStrokeAt(sheet,target);
let filled=null;
const nodes=flats.querySelectorAll("[data-part]");
for(let index=nodes.length-1;index>=0&&!filled;index--){
const node=nodes[index];
if(!coloringVisible(node)||!Object.prototype.hasOwnProperty.call(fills,node.dataset.part))continue;
try{if(node.isPointInFill(target))filled=node}catch(_){}
} }
return part&&art.contains(part)&&!part.hasAttribute("hidden")?part:null; const top=coloringTopLayer(strokeGroup,filled);
if(!top)return false;
if(top===filled){delete fills[filled.dataset.part];filled.setAttribute("fill",COLORING_PAPER)}
else{top.remove();strokes=readStrokes()}
completed=false;renderGuide();return true;
} }
function eraseFillAt(event){ /* De DOM is de waarheid voor wat er staat; de staat wordt daaruit gelezen
if(tool!=="eraser")return false; zodat wissen en herstellen niet uit elkaar kunnen lopen. */
const part=partBelow(event);if(!part||!Object.prototype.hasOwnProperty.call(fills,part.dataset.part))return false; function readStrokes(){
delete fills[part.dataset.part];part.setAttribute("fill","#fff");completed=false;return true; return [...sheet.querySelectorAll(".coloring-stroke")].map(group=>{
const wash=group.parentNode;
return{
part:wash.classList.contains("coloring-wash")?wash.dataset.for:null,
color:group.querySelector("path")?group.querySelector("path").getAttribute("stroke"):color,
segs:[...group.querySelectorAll("path")].map(path=>({w:Number(path.getAttribute("stroke-width")),d:path.getAttribute("d")}))
};
});
} }
const point=event=>{const rect=canvas.getBoundingClientRect();return{x:(event.clientX-rect.left)*canvas.width/rect.width,y:(event.clientY-rect.top)*canvas.height/rect.height}}; const round=value=>Math.round(value*10)/10;
let drawing=false,last=null,mid=null; let drawing=null;
function brushSegment(next,event){ function beginStroke(pt,event){
const width=coloringBrushWidth(size,event.pressure,event.pointerType),erase=tool==="eraser"; const width=round(coloringBrushWidth(size,event.pressure,event.pointerType));
if(erase)eraseFillAt(event); const region=clipStrokes?coloringRegionAt(sheet,domPoint(pt)):null;
ctx.save();ctx.globalCompositeOperation=erase?"destination-out":"source-over";ctx.strokeStyle=color;ctx.globalAlpha=erase?1:.72;ctx.lineCap="round";ctx.lineJoin="round";ctx.lineWidth=erase?width*3.5:width; drawing={
const nextMid={x:(last.x+next.x)/2,y:(last.y+next.y)/2};ctx.beginPath();ctx.moveTo(mid?mid.x:last.x,mid?mid.y:last.y);ctx.quadraticCurveTo(last.x,last.y,nextMid.x,nextMid.y);ctx.stroke(); part:region?region.dataset.part:null,color,
ctx.restore();last=next;mid=nextMid; segs:[{w:width,d:`M ${round(pt.x)} ${round(pt.y)} l .1 .1`}],
last:pt,mid:null,group:null
};
drawing.group=paintStroke({part:drawing.part,color:drawing.color,segs:drawing.segs});
} }
canvas.addEventListener("pointerdown",event=>{if(tool==="bucket"||(event.pointerType==="mouse"&&event.button!==0))return;event.preventDefault();canvas.setPointerCapture(event.pointerId);drawing=true;last=point(event);mid=null;brushSegment({x:last.x+.01,y:last.y+.01},event);completed=false;updateTexts()}); function extendStroke(pt,event){
canvas.addEventListener("pointermove",event=>{if(!drawing)return;(event.getCoalescedEvents?event.getCoalescedEvents():[event]).forEach(sample=>brushSegment(point(sample),sample))}); if(!drawing)return;
const stop=()=>{drawing=false;last=mid=null};canvas.addEventListener("pointerup",stop);canvas.addEventListener("pointercancel",stop); const width=round(coloringBrushWidth(size,event.pressure,event.pointerType));
reset.onclick=()=>{fills={};completed=false;clearInk();renderPage(false)}; const seg=drawing.segs[drawing.segs.length-1];
const nextMid={x:(drawing.last.x+pt.x)/2,y:(drawing.last.y+pt.y)/2};
const tail=` Q ${round(drawing.last.x)} ${round(drawing.last.y)} ${round(nextMid.x)} ${round(nextMid.y)}`;
if(Math.abs(width-seg.w)/seg.w>.15){
/* nieuwe penseelbreedte = nieuw subpad, aangehaakt op hetzelfde punt.
Binnen één streek staat de dekking op de groep, dus dat geeft geen naad. */
const next={w:width,d:`M ${round(drawing.last.x)} ${round(drawing.last.y)}`+tail};
drawing.segs.push(next);drawing.group.appendChild(strokePath(next,drawing.color));
}else{
seg.d+=tail;
drawing.group.lastElementChild.setAttribute("d",seg.d);
}
drawing.last=pt;drawing.mid=nextMid;
}
function endStroke(){
if(drawing)strokes=readStrokes();
drawing=null;
}
let erasing=false;
paper.addEventListener("pointerdown",event=>{
if(event.pointerType==="mouse"&&event.button!==0)return;
if(!sheet)return;
event.preventDefault();paper.setPointerCapture(event.pointerId);
const pt=point(event);
if(tool==="bucket"){fillAt(pt);updateTexts();return}
if(tool==="eraser"){erasing=true;eraseAt(pt);updateTexts();return}
beginStroke(pt,event);completed=false;updateTexts();
});
paper.addEventListener("pointermove",event=>{
if(!sheet)return;
if(erasing){eraseAt(point(event));return}
if(!drawing)return;
(event.getCoalescedEvents?event.getCoalescedEvents():[event]).forEach(sample=>extendStroke(point(sample),sample));
});
const stop=()=>{endStroke();erasing=false};
paper.addEventListener("pointerup",stop);paper.addEventListener("pointercancel",stop);
reset.onclick=()=>{fills={};strokes=[];legacyInk=null;completed=false;renderPage(true)};
done.onclick=()=>{completed=true;updateTexts();if(typeof opts.onProgress==="function")opts.onProgress({attempts:1,correct:1,stars:1,detail:{question:pageTitle(COLORING_PAGES.find(page=>page.id===pageId)),answer:T("colorFinished"),correct:true}})}; done.onclick=()=>{completed=true;updateTexts();if(typeof opts.onProgress==="function")opts.onProgress({attempts:1,correct:1,stars:1,detail:{question:pageTitle(COLORING_PAGES.find(page=>page.id===pageId)),answer:T("colorFinished"),correct:true}})};
document.addEventListener("langchange",()=>{rebuildThemeOptions();updateTexts();renderPage(true)}); /* eigen opruiming: de widget wordt bij elke niveauwijziging opnieuw gemonteerd */
const onLang=()=>{
if(!root.isConnected){document.removeEventListener("langchange",onLang);return}
rebuildThemeOptions();renderPage(true);
};
document.addEventListener("langchange",onLang);
rebuildThemeOptions();rebuildPageOptions(pageId);renderPage(true); rebuildThemeOptions();rebuildPageOptions(pageId);renderPage(true);
return{getState:()=>{ return{getState:()=>({
let ink=null;try{ink=canvas.toDataURL("image/png")}catch(_){/* een leeg canvas blijft gewoon herstelbaar */} theme,pageId,fills:{...fills},strokes:strokes.map(stroke=>({part:stroke.part,color:stroke.color,segs:stroke.segs.map(seg=>({w:seg.w,d:seg.d}))})),
return{theme,pageId,fills:{...fills},ink,tool,color,size,completed,yearGroup,level,managedLearningLevel:!!(initState&&initState.managedLearningLevel)}; tool,color,size,completed,guide,yearGroup,level,managedLearningLevel:!!(initState&&initState.managedLearningLevel)
}}; })};
} }

View file

@ -17,11 +17,42 @@ async function coloringContext(){
themes:COLORING_THEME_KEYS, themes:COLORING_THEME_KEYS,
pagesFor:coloringPagesFor, pagesFor:coloringPagesFor,
brushWidth:coloringBrushWidth, brushWidth:coloringBrushWidth,
safeColor:coloringSafeColor safeColor:coloringSafeColor,
clipsStrokes:coloringClipsStrokes,
paper:COLORING_PAPER
}`,context); }`,context);
return{source,api:context.__coloring}; return{source,api:context.__coloring};
} }
/* Telt de kleurvakken die op een niveau zichtbaar zijn. Een vak verdwijnt
alleen doordat een omhullende groep een hoger data-tier heeft - vandaar een
simpele tagscanner met een stapel. */
function visibleRegions(svg,level){
let count=0;const stack=[];
const tagPattern=/<(\/?)(g|path|circle|rect|ellipse|svg|defs)\b([^>]*)>/g;
let match;
while((match=tagPattern.exec(svg))){
const closing=match[1],name=match[2],attrs=match[3];
if(name==="g"){
if(closing)stack.pop();
else{
const tier=/data-tier="(\d)"/.exec(attrs);
stack.push(tier?Number(tier[1]):1);
}
continue;
}
if(closing||!/data-fillable="true"/.test(attrs))continue;
if(stack.every(tier=>tier<=level))count++;
}
return count;
}
const regionTags=svg=>[...svg.matchAll(/<(?:path|circle|rect|ellipse)\b[^>]*class="coloring-region[^"]*"[^>]*>/g)].map(match=>match[0]);
const bandOf=page=>page.years[0];
/* de "mag niet voorkomen"-controles gaan over code, niet over commentaar:
in de toelichting bóven het bestand staat juist wél waarom de oude
blendmode-laag weg is */
const stripComments=text=>text.replace(/\/\*[\s\S]*?\*\//g,"");
test('kleurplatenwidget laadt vóór het bord en is als creatieve leerlingwidget geregistreerd',async()=>{ test('kleurplatenwidget laadt vóór het bord en is als creatieve leerlingwidget geregistreerd',async()=>{
const [html,board,pupil,cast,core,css]=await Promise.all([ const [html,board,pupil,cast,core,css]=await Promise.all([
readFile('public/index.html','utf8'),readFile('public/js/board.js','utf8'), readFile('public/index.html','utf8'),readFile('public/js/board.js','utf8'),
@ -39,165 +70,139 @@ test('kleurplatenwidget laadt vóór het bord en is als creatieve leerlingwidget
assert.match(pupil,/PUPIL_WIDGET_CATS\s*=\s*\[[^\]]*"creatief"/); assert.match(pupil,/PUPIL_WIDGET_CATS\s*=\s*\[[^\]]*"creatief"/);
assert.match(cast,/CAST_WIDGET_CATEGORIES=new Set\(\[[^\]]*"creatief"/); assert.match(cast,/CAST_WIDGET_CATEGORIES=new Set\(\[[^\]]*"creatief"/);
assert.match(css,/\.coloring-paper[^}]*aspect-ratio:4\/3/); assert.match(css,/\.coloring-paper[^}]*aspect-ratio:4\/3/);
for(const key of ['wg_coloring','wg_coloring_d','catCreative','colorToolBrush','colorToolBucket','colorBucketHint','colorBrushHint']){ for(const key of ['wg_coloring','wg_coloring_d','catCreative','colorToolBrush','colorToolBucket',
'colorBucketHint','colorBrushHint','colorEraserHint','colorGuide',
'colorLevelHelp1','colorLevelHelp2','colorLevelHelp3']){
assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key); assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key);
} }
}); });
test('elk leerjaar heeft zeven samenhangende kleurplaten met gesloten vakken en drie detailniveaus',async()=>{ test('elk leerjaar heeft vijf platen met alle thema\'s, gesloten vakken en een kleurwijzer',async()=>{
const {api}=await coloringContext(); const {api}=await coloringContext();
const pages=JSON.parse(JSON.stringify(api.pages)); const pages=JSON.parse(JSON.stringify(api.pages));
assert.equal(pages.length,28); assert.equal(pages.length,20);
assert.equal(new Set(pages.map(page=>page.id)).size,pages.length); assert.equal(new Set(pages.map(page=>page.id)).size,pages.length);
const added=[
'farmyard','rainbow-weather','construction-site','forest-picnic',
'dinosaur-valley','spring-nest','fire-station','bee-meadow',
'coral-reef','mountain-camp','robot-workshop','canal-street',
'solar-observatory','savanna-web','japanese-garden'
];
assert.equal(added.filter(id=>pages.some(page=>page.id===id)).length,15);
for(let year=1;year<=8;year++){ for(let year=1;year<=8;year++){
const suitable=JSON.parse(JSON.stringify(api.pagesFor(year))); const suitable=JSON.parse(JSON.stringify(api.pagesFor(year)));
assert.equal(suitable.length,7,`groep ${year} heeft zeven kleurplaten`); assert.equal(suitable.length,5,`groep ${year} heeft vijf kleurplaten`);
assert.ok(suitable.every(page=>year>=page.years[0]&&year<=page.years[1])); assert.ok(suitable.every(page=>year>=page.years[0]&&year<=page.years[1]));
assert.equal(new Set(suitable.map(page=>page.theme)).size,5,`groep ${year} heeft alle vijf thema's`); assert.equal(new Set(suitable.map(page=>page.theme)).size,5,`groep ${year} heeft alle vijf thema's`);
} }
for(const page of pages){ for(const page of pages){
assert.ok(api.themes[page.theme],page.id+' heeft een bekend thema'); const svg=page.svg,id=page.id;
assert.ok(page.title.nl&&page.title.en,page.id+' heeft een tweetalige titel'); assert.ok(api.themes[page.theme],id+' heeft een bekend thema');
const parts=[...page.svg.matchAll(/data-part="([^"]+)"/g)].map(match=>match[1]); assert.ok(page.title.nl&&page.title.en,id+' heeft een tweetalige titel');
const details=(page.svg.match(/class="coloring-detail"/g)||[]).length; const parts=[...svg.matchAll(/data-part="([^"]+)"/g)].map(match=>match[1]);
const figures=(page.svg.match(/class="coloring-figure"/g)||[]).length; assert.equal(new Set(parts).size,parts.length,id+' heeft unieke kleurvakken');
const openFillablePaths=[...page.svg.matchAll(/<path\b[^>]*data-fillable="true"[^>]*data-part="([^"]+)"[^>]* d="([^"]+)"/g)] assert.match(svg,/class="coloring-sheet"[^>]*preserveAspectRatio="xMidYMid meet"/);
.filter(match=>!/[zZ]\s*$/.test(match[2])).map(match=>match[1]); assert.match(svg,new RegExp(`data-scene="${id}"`));
assert.ok(parts.length>=10,page.id+' heeft voldoende afzonderlijke kleurvakken'); assert.match(svg,/data-role="backdrop"|class="coloring-region coloring-backdrop"/,id+' heeft een achtergrondvak');
assert.equal(new Set(parts).size,parts.length,page.id+' heeft unieke kleurvakken'); assert.doesNotMatch(svg,/<script|on\w+=/i,id+' bevat geen scripts');
assert.deepEqual(openFillablePaths,[],page.id+' heeft alleen gesloten aanklikbare paden');
assert.ok(details>=1,page.id+' heeft herkenbaar, begrensd binnenlijnwerk'); /* alleen gesloten aanklikbare paden: anders vult de verfemmer een open vorm */
assert.ok(figures>=1,page.id+' groepeert onderdelen tot samenhangende figuren'); const openPaths=[...svg.matchAll(/<path\b[^>]*data-fillable="true"[^>]*d="([^"]+)"/g)]
assert.match(page.svg,/class="coloring-sheet"[^>]*preserveAspectRatio="xMidYMid meet"/); .filter(match=>!/[zZ]\s*$/.test(match[1].trim())).map(match=>match[1].slice(0,24));
assert.match(page.svg,/data-role="backdrop"/,page.id+' heeft een subtiel buitenkader'); assert.deepEqual(openPaths,[],id+' heeft alleen gesloten aanklikbare paden');
assert.match(page.svg,new RegExp(`data-scene="${page.id}"`));
assert.match(page.svg,/data-level="2"/); const regions=regionTags(svg);
assert.match(page.svg,/data-level="3"/); /* geen transform op een kleurvak: hetzelfde pad dient ook als clip voor de kwast */
assert.doesNotMatch(page.svg,/<script|on\w+=/i); assert.deepEqual(regions.filter(tag=>/transform=/.test(tag)),[],id+' heeft geen transform op een kleurvak');
/* elk vak draagt een voorgestelde kleur voor de kleurwijzer van niveau 1 */
assert.deepEqual(regions.filter(tag=>!/data-hint="#[0-9a-f]{6}"/.test(tag)),[],id+' heeft overal een kleurwijzerkleur');
/* data-tier hoort op een groep of een detaillijn, nooit op een los vak:
zo verdwijnt een rekwisiet altijd als geheel en blijft de plaat compleet */
assert.deepEqual(regions.filter(tag=>/data-tier=/.test(tag)),[],id+' zet data-tier nooit op een los kleurvak');
const total=regions.length,niveau1=visibleRegions(svg,1),niveau2=visibleRegions(svg,2),niveau3=visibleRegions(svg,3);
assert.equal(niveau2,total,`${id} toont op niveau 2 alle vakken`);
assert.equal(niveau3,niveau2,`${id} voegt op niveau 3 alleen lijnwerk toe, geen nieuwe vakken`);
assert.ok(niveau1>=8,`${id} houdt op niveau 1 een complete tekening (${niveau1} vakken)`);
assert.ok(niveau1<niveau2,`${id} voegt op niveau 2 vakken toe (${niveau1} -> ${niveau2})`);
/* de plaat groeit mee met de groepenband */
if(bandOf(page)===1)assert.ok(total<=27,`${id} blijft voor groep 1-2 overzichtelijk (${total})`);
if(bandOf(page)===7)assert.ok(total>=28,`${id} heeft voor groep 7-8 genoeg detail (${total})`);
assert.ok(total>=13,`${id} heeft genoeg kleurvakken (${total})`);
/* niveau 3 voegt uitsluitend fijn lijnwerk toe */
assert.match(svg,/class="coloring-detail" data-tier="3"/,id+' heeft fijn lijnwerk op niveau 3');
} }
const landmarks={
butterfly:['butterfly-forewing-left','butterfly-hindwing-right','butterfly-thorax','butterfly-abdomen'],
farmyard:['cow-silhouette','cow-muzzle','cow-horn-front','cow-udder','cow-tail'],
'forest-picnic':['rabbit-silhouette','rabbit-ear-front','rabbit-hind-leg','rabbit-forepaw'],
ocean:['fish-silhouette','fish-gill-cover','turtle-shell','turtle-front-flipper-near','turtle-head'],
garden:['snail-shell','snail-head-neck','ladybird-pronotum','ladybird-spot-left-one'],
'dinosaur-valley':['volcano','dinosaur-silhouette','dinosaur-belly','dinosaur-leg-0'],
'spring-nest':['bird-silhouette','bird-wing','bird-beak','nest-bowl'],
'bee-meadow':['bee-forewing','bee-abdomen','bee-thorax','bee-compound-eye','bee-mandible'],
rainforest:['macaw-silhouette','macaw-face-patch','macaw-upper-beak','macaw-tail-long'],
'coral-reef':['clownfish-body','clownfish-head-band','seahorse-silhouette','seahorse-crown'],
ecosystem:['frog-silhouette','frog-head-profile','frog-visible-eye','frog-tympanum','frog-near-hind-leg'],
'savanna-web':['lion-mane','lion-face','lion-tail','zebra-body','zebra-neck','zebra-mane']
};
for(const [id,names] of Object.entries(landmarks)){
const page=pages.find(item=>item.id===id);
names.forEach(name=>assert.ok(page.svg.includes(`data-part="${name}"`),`${id} mist ${name}`));
}
const organicContours={
butterfly:['butterfly-forewing-left','butterfly-abdomen'],
farmyard:['cow-silhouette','cow-muzzle'],
'forest-picnic':['rabbit-silhouette','rabbit-hind-leg'],
ocean:['fish-silhouette','turtle-shell','turtle-head'],
garden:['snail-head-neck','ladybird-body'],
'dinosaur-valley':['dinosaur-silhouette'],
'spring-nest':['bird-silhouette','bird-wing'],
'bee-meadow':['bee-abdomen','bee-thorax','bee-head'],
rainforest:['macaw-silhouette','macaw-wing','macaw-upper-beak'],
'coral-reef':['clownfish-body','seahorse-silhouette'],
ecosystem:['frog-silhouette','frog-head-profile','frog-near-hind-leg'],
'savanna-web':['lion-body','lion-face','zebra-body','zebra-head']
};
for(const [id,names] of Object.entries(organicContours)){
const page=pages.find(item=>item.id===id);
names.forEach(name=>{
const match=page.svg.match(new RegExp(`<path\\b[^>]*data-part="${name}"[^>]*d="([^"]+)"`));
assert.ok(match,`${id}/${name} gebruikt een getekend contoursilhouet`);
assert.ok((match[1].match(/C/g)||[]).length>=2,`${id}/${name} heeft vloeiende Bézierovergangen`);
});
}
const paintOrder={
farmyard:['cow-tail','cow-leg-0','cow-silhouette','cow-muzzle','cow-ear-back','cow-horn-front'],
'forest-picnic':['rabbit-tail','rabbit-ear-back','rabbit-ear-front','rabbit-silhouette','rabbit-hind-leg','rabbit-forepaw'],
ocean:['fish-tail','fish-dorsal-fin','fish-silhouette','fish-gill-cover','turtle-rear-flipper','turtle-front-flipper-near','turtle-shell','turtle-neck','turtle-head'],
garden:['ladybird-leg-back-a','ladybird-body','ladybird-pronotum','ladybird-wing-left','ladybird-spot-left-one'],
'dinosaur-valley':['dinosaur-leg-0','dinosaur-silhouette','dinosaur-belly'],
'spring-nest':['bird-tail-top','bird-silhouette','bird-wing','bird-beak'],
'bee-meadow':['bee-hindwing','bee-forewing','bee-abdomen','bee-stripe-one','bee-thorax','bee-head','bee-compound-eye','bee-mandible'],
rainforest:['macaw-tail-long','macaw-tail-short','macaw-silhouette','macaw-wing','macaw-face-patch','macaw-upper-beak','macaw-lower-beak'],
'coral-reef':['clownfish-tail','clownfish-dorsal-fin','clownfish-body','clownfish-head-band','seahorse-fin','seahorse-silhouette','seahorse-belly','seahorse-crown'],
ecosystem:['frog-far-hind-leg','frog-far-foot','frog-near-hind-leg','frog-near-foot','frog-silhouette','frog-head-profile','frog-visible-eye','frog-tympanum'],
'savanna-web':['lion-tail','lion-leg-0','lion-body','lion-mane','lion-face','zebra-tail','zebra-leg-0','zebra-body','zebra-neck','zebra-head','zebra-muzzle','zebra-mane'],
rocket:['rocket-exhaust-outer','rocket-fin-left','rocket-body','rocket-window'],
'fire-station':['engine-chassis','engine-body','engine-cab','engine-ladder','engine-wheel-left','engine-hub-left'],
'robot-workshop':['robot-leg-left','robot-upper-arm-left','robot-forearm-left','robot-body','robot-neck','robot-head'],
windmill:['mill-base','mill-cap','mill-sail-north','mill-hub']
};
for(const [id,names] of Object.entries(paintOrder)){
const page=pages.find(item=>item.id===id),positions=names.map(name=>page.svg.indexOf(`data-part="${name}"`));
assert.ok(positions.every(position=>position>=0),`${id} bevat alle aansluitende onderdelen`);
assert.deepEqual([...positions].sort((a,b)=>a-b),positions,`${id} schildert appendages achter het hoofdsilhouet`);
}
assert.equal(api.pagesFor(5,'science').length,2);
assert.equal(api.pagesFor(3,'science').length,1);
}); });
test('kwast reageert op pendruk en de contourlaag bewaart buiten- en anatomische binnenlijnen',async()=>{ test('kwast schildert in de SVG, blijft op de lage niveaus binnen de lijnen en houdt de contour scherp',async()=>{
const {source,api}=await coloringContext(); const {source,api}=await coloringContext();
assert.ok(api.brushWidth(7,.9,'pen')>api.brushWidth(7,.2,'pen')); assert.ok(api.brushWidth(7,.9,'pen')>api.brushWidth(7,.2,'pen'));
assert.equal(api.brushWidth(7,0,'mouse'),7); assert.equal(api.brushWidth(7,0,'mouse'),7);
assert.equal(api.safeColor('#AABBCC'),'#aabbcc'); assert.equal(api.safeColor('#AABBCC'),'#aabbcc');
assert.equal(api.safeColor('red'),'#e0554d'); assert.equal(api.safeColor('red'),'#e0554d');
for(const feature of ['getCoalescedEvents','event.pressure','erase?1:.72','quadraticCurveTo'])assert.ok(source.includes(feature),feature); /* binnen de lijnen is hulp: groep 1-4 op niveau 1 en 2, groep 5-8 op niveau 1 */
assert.equal(api.clipsStrokes(2,1),true);
assert.equal(api.clipsStrokes(2,2),true);
assert.equal(api.clipsStrokes(2,3),false);
assert.equal(api.clipsStrokes(4,2),true);
assert.equal(api.clipsStrokes(6,1),true);
assert.equal(api.clipsStrokes(6,2),false);
assert.equal(api.clipsStrokes(8,3),false);
for(const feature of ['getCoalescedEvents','event.pressure','COLORING_WASH_ALPHA','stroke-linecap'])
assert.ok(source.includes(feature),feature+' ontbreekt');
assert.match(source,/const savedTool=initState&&initState\.tool==="pencil"\?"brush"/); assert.match(source,/const savedTool=initState&&initState\.tool==="pencil"\?"brush"/);
assert.match(source,/\["brush","🖌️","colorToolBrush"\]/); assert.match(source,/\["brush","🖌️","colorToolBrush"\]/);
const css=await readFile('public/css/coloring.css','utf8'); /* schildersvolgorde: vak -> verflaag -> contour, en de contour vult nooit */
assert.match(css,/mix-blend-mode:multiply/); assert.match(source,/function expandRegions\(\)/);
assert.match(source,/class="coloring-art"><\/div><canvas[^>]+><\/canvas><div class="coloring-ink" aria-hidden="true"><\/div>/); assert.match(source,/region\.parentNode\.insertBefore\(wash,region\.nextSibling\)/);
assert.match(source,/coloringInkOverlay\(svg\)/); assert.match(source,/region\.parentNode\.insertBefore\(outline,wash\.nextSibling\)/);
assert.match(source,/const overlay=svg\.cloneNode\(true\)/); assert.match(source,/outline\.setAttribute\("fill","none"\)/);
assert.match(source,/overlay\.querySelectorAll\("\.coloring-recognition"\)\.forEach\(node=>node\.remove\(\)\)/); assert.match(source,/const COLORING_PAPER="#ffffff"/);
assert.doesNotMatch(source,/overlay\.querySelectorAll\("\.coloring-detail/); assert.equal(api.paper,'#ffffff');
assert.match(source,/node\.removeAttribute\("data-fillable"\)/); /* aanwijzen gebeurt meetkundig, niet door pointer-events om te zetten */
assert.match(source,/node\.setAttribute\("fill","#fff"\)/); assert.match(source,/isPointInFill/);
assert.match(source,/inkHost\.replaceChildren\(coloringInkOverlay\(svg\)\)/); assert.match(source,/isPointInStroke/);
assert.doesNotMatch(source,/coloring-ink-halo|feMorphology|feColorMatrix/); const code=stripComments(source);
assert.match(source,/class="coloring-region" data-fillable="true"/); assert.doesNotMatch(code,/document\.elementFromPoint/);
assert.match(source,/class="coloring-sheet"[^>]*preserveAspectRatio="xMidYMid meet"/); assert.doesNotMatch(code,/canvas\.style\.pointerEvents/);
assert.match(source,/fill-rule="evenodd" clip-rule="evenodd"[^>]*stroke-width="4\.8"[^>]*stroke-miterlimit="2"/); /* de oude, onzichtbare inktlaag mag niet terugkomen */
assert.match(css,/\.coloring-svg-lines\{shape-rendering:geometricPrecision\}/); assert.doesNotMatch(code,/coloringInkOverlay|coloring-ink-overlay|mix-blend-mode/);
assert.match(css,/\.coloring-region\[data-role="backdrop"\]\{stroke-width:2\.8\}/); assert.doesNotMatch(code,/<canvas/);
assert.match(css,/\.coloring-art\{z-index:1\}\.coloring-paper canvas\{z-index:2\}\.coloring-ink\{z-index:3;pointer-events:none\}/); assert.doesNotMatch(code,/toDataURL/);
assert.match(css,/\.coloring-svg-lines \.coloring-recognition\{display:none\}/);
assert.match(css,/\.coloring-ink-overlay\{pointer-events:none;shape-rendering:geometricPrecision;mix-blend-mode:multiply\}/);
assert.match(css,/\.coloring-ink-svg \.coloring-region\{fill:#fff!important;stroke:var\(--coloring-ink\);stroke-width:4\.8;stroke-opacity:1;paint-order:fill stroke\}/);
assert.match(css,/\.coloring-ink-svg \.coloring-detail\{stroke:var\(--coloring-detail\);stroke-width:2\.4/);
assert.doesNotMatch(css,/coloring-ink-halo/);
assert.match(css,/\.coloring-app\[data-tool="bucket"\] \.coloring-paper canvas\{pointer-events:none\}/);
assert.match(source,/canvas\.style\.pointerEvents=tool==="bucket"\?"none":"auto";art\.style\.pointerEvents=tool==="bucket"\?"auto":"none"/);
assert.match(source,/className="coloring-tool-label"/);
assert.match(source,/toolHost\.setAttribute\("role","toolbar"\)/);
assert.match(css,/\.coloring-tool-buttons button\{width:auto;min-width:72px/);
}); });
test('verfemmer vult een SVG-vak en de gum wist zowel kwast als opgeslagen vlakkleur',async()=>{ test('de opmaak legt nooit een dekkende laag over de verf',async()=>{
const css=await readFile('public/css/coloring.css','utf8'),rules=stripComments(css);
/* dit is de regressietest op de fout die de verf onzichtbaar maakte: een
CSS-regel op een kleurvak wint van het fill-attribuut van de verfemmer */
assert.doesNotMatch(rules,/\.coloring-region[^{]*\{[^}]*fill:/,'CSS mag geen fill op een kleurvak zetten');
assert.doesNotMatch(rules,/fill:[^;}]*!important/,'CSS mag een vulling nooit forceren');
assert.doesNotMatch(rules,/mix-blend-mode/,'geen blendmode-truc meer');
assert.doesNotMatch(rules,/coloring-ink-overlay|coloring-ink-svg|coloring-recognition/);
assert.doesNotMatch(rules,/\.coloring-paper canvas/);
/* de plaat is zelfvoorzienend en vangt zelf geen kliks: dat doet het papier */
assert.match(css,/\.coloring-sheet\{--coloring-ink:#1c293d;--coloring-detail:#2d3e56;[^}]*pointer-events:none/);
assert.match(css,/\.coloring-paper\{[^}]*touch-action:none/);
/* twee lijndiktes, zodat voor- en achtergrond leesbaar blijven */
assert.match(css,/\.coloring-outline\{stroke:var\(--coloring-ink,#1c293d\)/);
assert.match(css,/\.coloring-outline\.coloring-contour\{stroke-width:6\.4\}/);
assert.match(css,/\.coloring-outline\.coloring-flat\{stroke-width:3\.4\}/);
assert.match(css,/\.coloring-region\{stroke:none\}/);
assert.match(css,/\.coloring-stroke\{pointer-events:none\}/);
assert.match(css,/\.coloring-app\[data-tool="eraser"\] \.coloring-stroke\{pointer-events:stroke\}/);
});
test('verfemmer vult een vak, de gum pelt de bovenste laag af en de staat bewaart de streken',async()=>{
const {source}=await coloringContext(); const {source}=await coloringContext();
assert.match(source,/event\.target\.closest\("\[data-fillable\]\[data-part\]\"\)/); assert.match(source,/region\.setAttribute\("fill",color\);fills\[part\]=color/);
assert.match(source,/part\.setAttribute\("fill",color\)/); assert.match(source,/function coloringTopLayer/);
assert.match(source,/fills\[part\.dataset\.part\]=color/); assert.match(source,/DOCUMENT_POSITION_FOLLOWING/);
assert.match(source,/document\.elementFromPoint\(event\.clientX,event\.clientY\)/); assert.match(source,/delete fills\[filled\.dataset\.part\];filled\.setAttribute\("fill",COLORING_PAPER\)/);
assert.match(source,/Object\.prototype\.hasOwnProperty\.call\(fills,part\.dataset\.part\)/); assert.match(source,/else\{top\.remove\(\);strokes=readStrokes\(\)\}/);
assert.match(source,/delete fills\[part\.dataset\.part\];part\.setAttribute\("fill","#fff"\)/); assert.match(source,/function readStrokes\(\)/);
assert.match(source,/if\(erase\)eraseFillAt\(event\)/); assert.match(source,/strokes:strokes\.map\(stroke=>\(\{part:stroke\.part,color:stroke\.color/);
assert.match(source,/canvas\.toDataURL\("image\/png"\)/); /* oud werk uit de canvasversie blijft zichtbaar */
assert.match(source,/return\{theme,pageId,fills:\{\.\.\.fills\},ink,tool,color,size,completed,yearGroup,level/); assert.match(source,/class","coloring-legacy-ink/);
assert.match(source,/opts\.onProgress/); assert.match(source,/opts\.onProgress/);
/* kleurwijzer: hulp op niveau 1, uitdaging op niveau 3 */
assert.match(source,/function renderGuide\(\)/);
assert.match(source,/guideBtn\.hidden=level>=3/);
assert.match(source,/T\("colorLevelHelp"\+level\)/);
assert.match(source,/document\.removeEventListener\("langchange",onLang\)/);
}); });
test('creatief niveau is beschikbaar per klas, leerling en thuiskind',async()=>{ test('creatief niveau is beschikbaar per klas, leerling en thuiskind',async()=>{