@autoaiai/aicut 0.1.7 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANUAL.md +94 -84
- package/package.json +1 -1
- package/product.json +2 -1
- package/skills/aicut/SKILL.md +4 -4
- package/skills/aicut/references/commands.md +15 -15
- package/skills/aicut/references/film-format.md +1 -1
- package/skills/aicut/references/recipes.md +4 -4
- package/skills/aicut/references/troubleshooting.md +2 -2
- package/skills/aicut-motion/SKILL.md +75 -9
- package/skills/aicut-motion/THIRD_PARTY_NOTICES.md +14 -0
- package/skills/aicut-motion/assets/atlas/lib/harness.js +116 -0
- package/skills/aicut-motion/assets/atlas/lib/sound.js +274 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/code.js +937 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/001-flat-vector-science/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/code.js +909 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/002-watercolor-wash/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/code.js +881 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/003-shadow-puppet-theatre/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/code.js +897 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/004-whiteboard-explainer/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/code.js +814 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/005-psychedelic-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/code.js +990 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/006-tech-glow-explainer/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/code.js +994 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/007-papercraft-models/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/code.js +792 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/008-1940s-film-noir/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/code.js +840 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/009-manim-style-math/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/code.js +1126 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/010-illuminated-manuscript/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/code.js +854 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/011-editorial-essay-collage/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/code.js +1124 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/012-bioluminescent-deep-sea/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/code.js +728 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/013-product-launch-reveal/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/code.js +664 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/014-halftone-pop-panel/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/code.js +991 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/015-isometric-pastel/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/code.js +801 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/016-oscilloscope-vector-art/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/code.js +692 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/017-risograph-zine/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/code.js +756 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/018-claymation-set/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/code.js +922 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/019-neon-sign-at-night/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/code.js +852 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/020-kawaii-anime/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/code.js +724 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/021-ukiyo-e-woodblock/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/code.js +793 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/022-16-bit-pixel-odyssey/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/code.js +846 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/023-chalkboard-lesson/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/code.js +1008 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/024-cut-paper-diorama/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/code.js +680 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/025-cathedral-glass/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/code.js +633 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/026-editorial-chart/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/code.js +757 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/027-synthwave-outrun/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/code.js +828 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/028-origami-studio/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/code.js +697 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/029-linocut-relief-print/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/code.js +687 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/030-holographic-foil-card/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/code.js +727 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/031-voxel-diorama/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/code.js +903 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/032-sumi-e-ink-wash/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/code.js +662 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/033-etch-a-sketch-toy/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/code.js +951 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/034-cosmic-star-chart/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/code.js +759 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/035-punk-zine-collage/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/code.js +738 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/036-thermal-camera/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/code.js +793 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/037-engineering-blueprint/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/code.js +673 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/038-impasto-night/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/code.js +907 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/039-win95-desktop/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/code.js +833 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/040-low-poly-faceted/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/code.js +845 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/041-manga-page/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/code.js +496 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/042-wood-type-letterpress/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/code.js +635 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/043-light-painting/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/code.js +799 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/044-felt-patchwork/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/code.js +547 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/045-phosphor-terminal/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/code.js +592 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/046-glassmorphism-ui/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/code.js +768 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/047-tarot-card/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/code.js +666 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/048-transit-map/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/code.js +559 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/049-gouache-travel-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/code.js +943 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/050-napkin-sketch/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/code.js +606 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/051-art-deco-foil/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/code.js +730 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/052-radiograph-on-lightbox/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/code.js +782 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/053-vaporwave/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/code.js +504 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/054-ebru-paper-marbling/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/code.js +517 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/055-isotype-pictograms/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/code.js +713 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/056-soft-3d-clay/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/code.js +717 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/057-cross-stitch-sampler/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/code.js +540 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/058-constructivist-propaganda/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/code.js +524 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/059-pocket-monochrome/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/code.js +934 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/060-botanical-plate/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/code.js +690 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/061-kinetic-type/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/code.js +612 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/062-byzantine-mosaic/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/code.js +666 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/063-y2k-liquid-chrome/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/code.js +651 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/064-sticky-note-wall/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/code.js +656 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/065-vintage-newsprint/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/code.js +672 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/066-hidden-line-wireframe/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/code.js +548 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/067-memphis-group/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/code.js +989 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/068-ballpoint-notebook/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/code.js +440 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/069-op-art-sphere/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/code.js +895 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/070-topographic-survey/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/code.js +563 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/071-pointillist-afternoon/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/code.js +627 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/072-vector-arcade-1980/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/code.js +1096 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/073-patent-drawing/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/code.js +595 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/074-bauhaus-poster/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/code.js +665 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/075-bitmap-paint-doodle/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/code.js +645 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/076-nail-thread-string-art/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/code.js +436 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/077-ridgeline/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/code.js +1237 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/078-sketchbook-graphite/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/code.js +622 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/079-teletext-page/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/code.js +524 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/080-silkscreen-grid/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/code.js +689 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/081-guilloch-security-print/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/code.js +574 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/082-ascii-demoscene/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/code.js +469 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/083-international-typographic-style/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/code.js +531 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/084-flow-field/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/code.js +675 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/085-macpaint-1984/style.json +13 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/code.js +609 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/poster.jpg +0 -0
- package/skills/aicut-motion/assets/atlas/styles/086-multi-scale-truchet/style.json +13 -0
- package/skills/aicut-motion/references/design-systems/README.md +1 -1
- package/skills/aicut-motion/references/motion-delivery.md +55 -0
- package/skills/aicut-motion/references/pages.md +1 -1
- package/skills/aicut-motion/references/style-index.json +690 -0
- package/skills/aicut-motion/references/style-research.md +73 -0
- package/skills/aicut-motion/references/style-selection.md +71 -0
- package/skills/aicut-motion/scripts/atlas-viewer.html +195 -0
- package/skills/aicut-motion/scripts/atlas.mjs +109 -0
- package/studio/client.mjs +3 -3
- package/studio/server/fetch-github.mjs +14 -3
- package/studio/server/server.mjs +71 -6
- package/studio/server/skill-summaries.mjs +60 -0
- package/studio/server/skills.mjs +121 -7
- package/studio/server/styles-store.mjs +235 -0
- package/studio/server/template-posters.mjs +2 -2
- package/studio/server/templates-page.mjs +93 -93
- package/studio/server/templates.mjs +32 -7
- package/studio/ui/dist/assets/ExportCenter-DasulhAv.js +1 -0
- package/studio/ui/dist/assets/SettingsModal-BZw0Hz2_.js +2 -0
- package/studio/ui/dist/assets/apple-touch-icon-ClxQ7j-O.png +0 -0
- package/studio/ui/dist/assets/es-C8XPZARI.js +1 -0
- package/studio/ui/dist/assets/favicon-Blwx8Trs.png +0 -0
- package/studio/ui/dist/assets/index-BjJVmBzB.js +1623 -0
- package/studio/ui/dist/assets/index-C3BfKZFO.css +1 -0
- package/studio/ui/dist/assets/ja-BeNkdaEj.js +1 -0
- package/studio/ui/dist/assets/ko-DcTVhdzK.js +1 -0
- package/studio/ui/dist/assets/zh-CN-Cg51ctbL.js +1 -0
- package/studio/ui/dist/index.html +4 -3
- package/studio/ui/dist/assets/ExportCenter-F-2vF-lQ.js +0 -1
- package/studio/ui/dist/assets/SettingsModal-BgVYguNu.js +0 -2
- package/studio/ui/dist/assets/es-BRUzvuoM.js +0 -1
- package/studio/ui/dist/assets/index-Bhg-9dA3.css +0 -1
- package/studio/ui/dist/assets/index-CWur1Iw9.js +0 -1625
- package/studio/ui/dist/assets/ja-Cth-uDcW.js +0 -1
- package/studio/ui/dist/assets/ko-upvw591D.js +0 -1
- package/studio/ui/dist/assets/zh-CN-BCfJFazB.js +0 -1
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 57,
|
|
3
|
+
"name": "Cross-Stitch Sampler",
|
|
4
|
+
"nameZh": "十字绣样本",
|
|
5
|
+
"family": "Paper & Craft",
|
|
6
|
+
"familyZh": "纸艺与手工",
|
|
7
|
+
"description": "Pixel art worked as real counted cross-stitch on cream aida: every cell is an X of two-strand floss with sheen and twist, top legs all leaning the same way, dark backstitch outlines, all stretched in a wooden hoop.",
|
|
8
|
+
"motionNotes": "Motion: The design fills in stitch by stitch in chart order, the needle hopping row to row trailing its floss, while the hoop drifts gently.",
|
|
9
|
+
"explainerNotes": "Explainer: One long sampler slides through the hoop from motif to motif; everything is stitched cell by cell with the needle chasing the newest stitch, arrows and outlines are backstitch laid one hole at a time, numbers are re-stitched in a 3x5 sampler font, and the wrong guess gets a red running-stitch ring.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "cross-stitch",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
{
|
|
2
|
+
const fieldNoise = (w, h, seed, cell, oct, sx = 1, sy = 1) => {
|
|
3
|
+
const nz = H.makeNoise(seed), gw = Math.ceil(w / cell) + 2, gh = Math.ceil(h / cell) + 2, g = new Float32Array(gw * gh);
|
|
4
|
+
for (let j = 0; j < gh; j++) for (let i = 0; i < gw; i++) g[j * gw + i] = nz.fbm2(i * 0.35 * sx, j * 0.35 * sy, oct);
|
|
5
|
+
const out = new Float32Array(w * h);
|
|
6
|
+
for (let y = 0; y < h; y++) {
|
|
7
|
+
const fy = y / cell, j = fy | 0, ty = fy - j;
|
|
8
|
+
for (let x = 0; x < w; x++) {
|
|
9
|
+
const fx = x / cell, i = fx | 0, tx = fx - i, k = j * gw + i;
|
|
10
|
+
const a = g[k] + (g[k + 1] - g[k]) * tx, b = g[k + gw] + (g[k + gw + 1] - g[k + gw]) * tx;
|
|
11
|
+
out[y * w + x] = a + (b - a) * ty;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
};
|
|
16
|
+
const inkify = (layer, seed, mottle, speck) => {
|
|
17
|
+
const c = layer.getContext('2d'), w = layer.width, h = layer.height, img = c.getImageData(0, 0, w, h), d = img.data;
|
|
18
|
+
const n1 = fieldNoise(w, h, seed, 4, 3), n2 = fieldNoise(w, h, seed + 9, 36, 3), r = H.rng(seed + 3);
|
|
19
|
+
for (let i = 0, p = 0; i < d.length; i += 4, p++) {
|
|
20
|
+
if (!d[i + 3]) continue;
|
|
21
|
+
let a = 1 - mottle * Math.max(0, n1[p] * 1.7 + n2[p] * 1.1);
|
|
22
|
+
if (r() < speck * (0.3 + Math.max(0, n2[p]) * 4)) a *= 0.2;
|
|
23
|
+
d[i + 3] *= H.clamp(a, 0, 1);
|
|
24
|
+
}
|
|
25
|
+
c.putImageData(img, 0, 0);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let PHOTO = null;
|
|
29
|
+
const CACHE = {}; const CF = (k, ...a) => CACHE[k] || (CACHE[k] = fieldNoise(...a));
|
|
30
|
+
const RED = '#c8281e', BLACK = '#1d1a17', CREAM = '#e9dcbc';
|
|
31
|
+
|
|
32
|
+
// grayscale "photograph" of a robot shouting into a megaphone (profile, facing right)
|
|
33
|
+
const drawPhoto = (c, t) => {
|
|
34
|
+
const lg = (x0, y0, x1, y1, stops) => { const g = c.createLinearGradient(x0, y0, x1, y1); stops.forEach(([o, v]) => g.addColorStop(o, `rgb(${v},${v},${v})`)); return g; };
|
|
35
|
+
const rg = (x, y, r0, r1, stops) => { const g = c.createRadialGradient(x, y, r0, x, y, r1); stops.forEach(([o, v]) => g.addColorStop(o, `rgb(${v},${v},${v})`)); return g; };
|
|
36
|
+
// torso / shoulders
|
|
37
|
+
c.fillStyle = lg(150, 470, 560, 720, [[0, 70], [0.45, 150], [0.75, 210], [1, 120]]);
|
|
38
|
+
c.beginPath(); c.moveTo(120, 760); c.lineTo(170, 520); c.quadraticCurveTo(200, 470, 290, 458); c.lineTo(470, 458); c.quadraticCurveTo(560, 470, 590, 540); c.lineTo(640, 760); c.closePath(); c.fill();
|
|
39
|
+
// chest plate seams + rivets
|
|
40
|
+
c.strokeStyle = 'rgb(40,40,40)'; c.lineWidth = 5;
|
|
41
|
+
c.beginPath(); c.moveTo(380, 470); c.lineTo(390, 760); c.stroke();
|
|
42
|
+
c.beginPath(); c.moveTo(200, 560); c.lineTo(600, 560); c.stroke();
|
|
43
|
+
c.fillStyle = 'rgb(30,30,30)';
|
|
44
|
+
for (let i = 0; i < 7; i++) { c.beginPath(); c.arc(230 + i * 55, 585, 6, 0, 7); c.fill(); }
|
|
45
|
+
c.fillStyle = rg(470, 640, 5, 90, [[0, 250], [1, 90]]); c.beginPath(); c.arc(470, 650, 70, 0, 7); c.fill(); // power core
|
|
46
|
+
c.fillStyle = 'rgb(25,25,25)'; c.beginPath(); c.arc(470, 650, 38, 0, 7); c.fill();
|
|
47
|
+
c.fillStyle = rg(462, 640, 2, 30, [[0, 255], [1, 120]]); c.beginPath(); c.arc(470, 650, 26, 0, 7); c.fill();
|
|
48
|
+
// neck rings
|
|
49
|
+
for (let i = 0; i < 4; i++) {
|
|
50
|
+
c.fillStyle = lg(320, 0, 440, 0, [[0, 50], [0.6, 200], [1, 90]]);
|
|
51
|
+
c.beginPath(); c.ellipse(380, 445 - i * 16, 58, 13, 0, 0, 7); c.fill();
|
|
52
|
+
}
|
|
53
|
+
// head (profile)
|
|
54
|
+
c.fillStyle = lg(250, 160, 520, 420, [[0, 60], [0.35, 140], [0.7, 225], [1, 160]]);
|
|
55
|
+
c.beginPath();
|
|
56
|
+
c.moveTo(270, 400); c.lineTo(250, 250); c.quadraticCurveTo(250, 150, 360, 140); c.lineTo(450, 140);
|
|
57
|
+
c.quadraticCurveTo(510, 145, 520, 210); c.lineTo(535, 300); c.lineTo(520, 345); c.lineTo(500, 400); c.closePath(); c.fill();
|
|
58
|
+
// jaw shadow
|
|
59
|
+
c.fillStyle = 'rgba(20,20,20,0.55)'; c.beginPath(); c.moveTo(270, 400); c.lineTo(500, 400); c.lineTo(515, 360); c.lineTo(290, 370); c.closePath(); c.fill();
|
|
60
|
+
// visor
|
|
61
|
+
c.fillStyle = lg(380, 200, 540, 260, [[0, 20], [0.7, 40], [1, 90]]);
|
|
62
|
+
c.beginPath(); c.moveTo(380, 205); c.lineTo(522, 205); c.lineTo(530, 262); c.lineTo(390, 262); c.closePath(); c.fill();
|
|
63
|
+
c.fillStyle = rg(480, 228, 2, 40, [[0, 255], [1, 40]]); c.beginPath(); c.ellipse(480, 230, 34, 14, 0, 0, 7); c.fill(); // glowing eye
|
|
64
|
+
c.strokeStyle = 'rgb(250,250,250)'; c.lineWidth = 4; c.beginPath(); c.moveTo(395, 212); c.lineTo(515, 212); c.stroke(); // visor glint
|
|
65
|
+
// ear disc
|
|
66
|
+
c.fillStyle = rg(330, 270, 4, 52, [[0, 240], [0.5, 150], [1, 40]]); c.beginPath(); c.arc(330, 275, 48, 0, 7); c.fill();
|
|
67
|
+
c.strokeStyle = 'rgb(30,30,30)'; c.lineWidth = 5; c.beginPath(); c.arc(330, 275, 30, 0, 7); c.stroke();
|
|
68
|
+
c.fillStyle = 'rgb(20,20,20)'; c.beginPath(); c.arc(330, 275, 10, 0, 7); c.fill();
|
|
69
|
+
// antenna bolts
|
|
70
|
+
c.fillStyle = lg(300, 100, 330, 100, [[0, 60], [1, 220]]); c.fillRect(300, 92, 18, 52);
|
|
71
|
+
c.fillStyle = rg(309, 88, 2, 18, [[0, 250], [1, 50]]); c.beginPath(); c.arc(309, 88, 16, 0, 7); c.fill();
|
|
72
|
+
// mouth grille
|
|
73
|
+
c.fillStyle = 'rgb(25,25,25)'; c.fillRect(440, 300, 70, 46);
|
|
74
|
+
c.fillStyle = 'rgb(170,170,170)'; for (let i = 0; i < 4; i++) c.fillRect(444, 304 + i * 11, 62, 4);
|
|
75
|
+
// megaphone cone (from mouth to upper right)
|
|
76
|
+
const mx0 = 500, my0 = 322, mx1 = 690, my1 = 228, a = Math.atan2(my1 - my0, mx1 - mx0), nx = -Math.sin(a), ny = Math.cos(a);
|
|
77
|
+
const r0 = 24, r1 = 92;
|
|
78
|
+
c.fillStyle = lg(mx0 + nx * r1, my0 + ny * r1, mx0 - nx * r1, my0 - ny * r1, [[0, 50], [0.35, 140], [0.62, 245], [0.8, 190], [1, 80]]);
|
|
79
|
+
c.beginPath(); c.moveTo(mx0 + nx * r0, my0 + ny * r0); c.lineTo(mx1 + nx * r1, my1 + ny * r1); c.lineTo(mx1 - nx * r1, my1 - ny * r1); c.lineTo(mx0 - nx * r0, my0 - ny * r0); c.closePath(); c.fill();
|
|
80
|
+
// bell rim
|
|
81
|
+
c.save(); c.translate(mx1, my1); c.rotate(a);
|
|
82
|
+
c.fillStyle = 'rgb(35,35,35)'; c.beginPath(); c.ellipse(0, 0, 26, r1 + 4, 0, 0, 7); c.fill();
|
|
83
|
+
c.fillStyle = rg(6, 0, 2, 70, [[0, 30], [1, 110]]); c.beginPath(); c.ellipse(4, 0, 18, r1 - 6, 0, 0, 7); c.fill();
|
|
84
|
+
c.restore();
|
|
85
|
+
// handle + raised arm
|
|
86
|
+
c.fillStyle = lg(420, 360, 520, 460, [[0, 80], [0.5, 210], [1, 70]]);
|
|
87
|
+
c.beginPath(); c.moveTo(560, 300); c.lineTo(588, 290); c.lineTo(560, 430); c.lineTo(520, 470); c.lineTo(470, 470); c.lineTo(530, 400); c.closePath(); c.fill();
|
|
88
|
+
c.fillStyle = 'rgb(40,40,40)'; c.beginPath(); c.arc(560, 330, 16, 0, 7); c.fill();
|
|
89
|
+
return { mx1, my1, a };
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// cut out + halftone-screen a grayscale "photograph" (drawn by fn) once; returns photo, dot layer and cut-out rim
|
|
93
|
+
const screenPhoto = (fn, W, HH, seed = 19, S = 6.2, gam = 1) => {
|
|
94
|
+
const PH = H.layer(W, HH), pc = PH.getContext('2d');
|
|
95
|
+
fn(pc);
|
|
96
|
+
const pdat = pc.getImageData(0, 0, W, HH).data;
|
|
97
|
+
const HT = H.layer(W, HH), hc = HT.getContext('2d');
|
|
98
|
+
const th = Math.PI / 4, cs = Math.cos(th), sn = Math.sin(th);
|
|
99
|
+
hc.fillStyle = BLACK; hc.beginPath();
|
|
100
|
+
const ext = 1100;
|
|
101
|
+
for (let v = -ext / S; v < ext / S; v++) for (let u = -ext / S; u < ext / S * 1.6; u++) {
|
|
102
|
+
const x = (u * cs - v * sn) * S + 120, y = (u * sn + v * cs) * S - 400;
|
|
103
|
+
if (x < 0 || y < 0 || x >= W || y >= HH) continue;
|
|
104
|
+
const k = ((y | 0) * W + (x | 0)) * 4;
|
|
105
|
+
const al = pdat[k + 3] / 255; if (al < 0.5) continue;
|
|
106
|
+
const lum = gam === 1 ? pdat[k] / 255 : Math.pow(pdat[k] / 255, gam); // gam < 1 opens the midtones for small pastes
|
|
107
|
+
const dk = Math.pow(1 - lum, 0.9);
|
|
108
|
+
const rad = S * 0.62 * Math.sqrt(dk) + 0.35;
|
|
109
|
+
hc.moveTo(x + rad, y); hc.arc(x, y, rad, 0, Math.PI * 2);
|
|
110
|
+
}
|
|
111
|
+
hc.fill();
|
|
112
|
+
const SIL = H.layer(W, HH), sc = SIL.getContext('2d');
|
|
113
|
+
sc.drawImage(PH, 0, 0); sc.globalCompositeOperation = 'source-in'; sc.fillStyle = '#efe5cc'; sc.fillRect(0, 0, W, HH);
|
|
114
|
+
inkify(HT, seed, 0.18, 0.004);
|
|
115
|
+
return { PH, HT, SIL };
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// ===================== EXPLAINER: "How does AI learn?" =====================
|
|
119
|
+
// Agitprop sequence: Lissitzky's red wedge drives across the sheet between scenes, slogans slam on slanted
|
|
120
|
+
// bars, arrows are heavy bars with wedge heads, numbers are stencil-tall Anton, photos are halftone cut-outs.
|
|
121
|
+
const EXC = {};
|
|
122
|
+
const exPaperC = (W, HH) => EXC.paper || (EXC.paper = (() => {
|
|
123
|
+
const cv = H.layer(W, HH), x = cv.getContext('2d'), img = x.createImageData(W, HH), pd = img.data;
|
|
124
|
+
const lo = CF('lo', W, HH, 41, 22, 4), fib = CF('fib', W, HH, 42, 2, 2, 4, 0.5), r0 = H.rng(5), base = H.hex(CREAM), tan = H.hex('#b99a62');
|
|
125
|
+
for (let y = 0, p = 0; y < HH; y++) for (let xx = 0; xx < W; xx++, p++) {
|
|
126
|
+
const ex = Math.min(xx, W - xx) / W, ey = Math.min(y, HH - y) / HH;
|
|
127
|
+
const edge = Math.pow(1 - H.clamp(Math.min(ex * 4, ey * 3) + lo[p] * 0.2, 0, 1), 3), k = 0.4 * edge + 0.16 * Math.max(0, lo[p]);
|
|
128
|
+
const s = 1 + fib[p] * 0.04 + (r0() - 0.5) * 0.05, i = p * 4;
|
|
129
|
+
pd[i] = (base[0] + (tan[0] - base[0]) * k) * s; pd[i + 1] = (base[1] + (tan[1] - base[1]) * k) * s; pd[i + 2] = (base[2] + (tan[2] - base[2]) * k) * s; pd[i + 3] = 255;
|
|
130
|
+
}
|
|
131
|
+
x.putImageData(img, 0, 0); return cv;
|
|
132
|
+
})());
|
|
133
|
+
const exInk = (layer, seed, mottle, speck = 0) => { // cached-noise version of inkify (mottle + pinhole voids)
|
|
134
|
+
const c = layer.getContext('2d'), w = layer.width, h = layer.height, img = c.getImageData(0, 0, w, h), d = img.data;
|
|
135
|
+
const n1 = CF('ei1', w, h, seed, 4, 3), n2 = CF('ei2', w, h, seed + 9, 36, 3);
|
|
136
|
+
const rnd = CACHE['rnd' + w] || (CACHE['rnd' + w] = (() => { const r = H.rng(seed + 3), a = new Float32Array(w * h); for (let i = 0; i < a.length; i++) a[i] = r(); return a; })());
|
|
137
|
+
for (let i = 0, p = 0; i < d.length; i += 4, p++) {
|
|
138
|
+
if (!d[i + 3]) continue;
|
|
139
|
+
const m = n2[p] > 0 ? n2[p] : 0;
|
|
140
|
+
let a = 1 - mottle * Math.max(0, n1[p] * 1.7 + n2[p] * 1.1);
|
|
141
|
+
if (rnd[p] < speck * (0.3 + m * 4)) a *= 0.2;
|
|
142
|
+
d[i + 3] *= a < 0 ? 0 : a > 1 ? 1 : a;
|
|
143
|
+
}
|
|
144
|
+
c.putImageData(img, 0, 0);
|
|
145
|
+
};
|
|
146
|
+
// grayscale studio "photos" of a cat and a dog, for the halftone screen
|
|
147
|
+
const drawAnimal = (kind) => (c) => {
|
|
148
|
+
const g = (x0, y0, x1, y1, st) => { const gr = c.createLinearGradient(x0, y0, x1, y1); st.forEach(([o, v]) => gr.addColorStop(o, `rgb(${v},${v},${v})`)); return gr; };
|
|
149
|
+
c.save(); c.translate(300, 300);
|
|
150
|
+
if (kind === 'cat') {
|
|
151
|
+
c.fillStyle = g(-120, -100, 120, 140, [[0, 60], [0.5, 150], [1, 230]]);
|
|
152
|
+
c.beginPath(); c.ellipse(0, 70, 95, 110, 0, 0, 7); c.fill();
|
|
153
|
+
c.beginPath(); c.arc(0, -60, 72, 0, 7); c.fill();
|
|
154
|
+
c.beginPath(); c.moveTo(-66, -80); c.lineTo(-58, -160); c.lineTo(-14, -118); c.closePath(); c.fill();
|
|
155
|
+
c.beginPath(); c.moveTo(66, -80); c.lineTo(58, -160); c.lineTo(14, -118); c.closePath(); c.fill();
|
|
156
|
+
c.fillStyle = 'rgb(240,240,240)'; c.beginPath(); c.ellipse(-26, -64, 14, 10, 0, 0, 7); c.ellipse(26, -64, 14, 10, 0, 0, 7); c.fill();
|
|
157
|
+
c.fillStyle = 'rgb(10,10,10)'; c.beginPath(); c.ellipse(-26, -64, 4, 9, 0, 0, 7); c.ellipse(26, -64, 4, 9, 0, 0, 7); c.fill();
|
|
158
|
+
c.beginPath(); c.moveTo(-8, -38); c.lineTo(8, -38); c.lineTo(0, -28); c.closePath(); c.fill();
|
|
159
|
+
c.strokeStyle = 'rgb(40,40,40)'; c.lineWidth = 12; c.beginPath(); c.moveTo(80, 150); c.quadraticCurveTo(170, 140, 140, 40); c.stroke();
|
|
160
|
+
} else {
|
|
161
|
+
c.fillStyle = g(-120, -100, 120, 140, [[0, 230], [0.5, 160], [1, 70]]);
|
|
162
|
+
c.beginPath(); c.ellipse(0, 80, 100, 100, 0, 0, 7); c.fill();
|
|
163
|
+
c.beginPath(); c.arc(0, -55, 75, 0, 7); c.fill();
|
|
164
|
+
c.fillStyle = 'rgb(50,50,50)'; c.beginPath(); c.ellipse(-78, -40, 26, 62, 0.35, 0, 7); c.ellipse(78, -40, 26, 62, -0.35, 0, 7); c.fill();
|
|
165
|
+
c.fillStyle = 'rgb(225,225,225)'; c.beginPath(); c.ellipse(0, -18, 40, 30, 0, 0, 7); c.fill();
|
|
166
|
+
c.fillStyle = 'rgb(10,10,10)'; c.beginPath(); c.arc(-28, -70, 9, 0, 7); c.arc(28, -70, 9, 0, 7); c.fill();
|
|
167
|
+
c.beginPath(); c.ellipse(0, -30, 15, 10, 0, 0, 7); c.fill();
|
|
168
|
+
c.fillStyle = 'rgb(120,40,40)'; c.beginPath(); c.ellipse(0, 4, 12, 16, 0, 0, Math.PI); c.fill();
|
|
169
|
+
}
|
|
170
|
+
c.restore();
|
|
171
|
+
};
|
|
172
|
+
const exPhotos = (W, HH) => EXC.ph || (EXC.ph = {
|
|
173
|
+
robot: PHOTO || (PHOTO = screenPhoto((pc) => drawPhoto(pc, 0), W, HH)),
|
|
174
|
+
cat: screenPhoto(drawAnimal('cat'), 600, 600, 23), dog: screenPhoto(drawAnimal('dog'), 600, 600, 29),
|
|
175
|
+
catS: screenPhoto(drawAnimal('cat'), 600, 600, 23, 11, 0.7), dogS: screenPhoto(drawAnimal('dog'), 600, 600, 29, 11, 0.7),
|
|
176
|
+
});
|
|
177
|
+
// paste a screened photo: cream cut-out rim, faint tone, dots (all multiplied on paper)
|
|
178
|
+
const paste = (ctx, P, x, y, s, rot = 0) => {
|
|
179
|
+
ctx.save(); ctx.translate(x, y); ctx.rotate(rot); ctx.scale(s, s);
|
|
180
|
+
const ro = Math.max(1, 3.2 / Math.max(0.05, s) / 4);
|
|
181
|
+
for (const [dx, dy] of [[-4, 0], [4, 0], [0, -4], [0, 4], [-3, -3], [3, 3], [3, -3], [-3, 3]]) ctx.drawImage(P.SIL, dx * ro, dy * ro);
|
|
182
|
+
ctx.drawImage(P.SIL, 0, 0);
|
|
183
|
+
ctx.globalAlpha = 0.18; ctx.globalCompositeOperation = 'multiply'; ctx.drawImage(P.PH, 0, 0);
|
|
184
|
+
ctx.globalAlpha = 1; ctx.drawImage(P.HT, 1.5, -1);
|
|
185
|
+
ctx.restore();
|
|
186
|
+
};
|
|
187
|
+
// heavy bar arrow with a wedge head
|
|
188
|
+
const barArrow = (c, x0, y0, x1, y1, p, w, col) => {
|
|
189
|
+
if (p <= 0) return; const a = Math.atan2(y1 - y0, x1 - x0), l = Math.hypot(x1 - x0, y1 - y0) * p;
|
|
190
|
+
c.save(); c.translate(x0, y0); c.rotate(a); c.fillStyle = col; c.fillRect(0, -w / 2, Math.max(0, l - w * 2.2), w);
|
|
191
|
+
c.beginPath(); c.moveTo(l, 0); c.lineTo(l - w * 2.6, -w * 1.4); c.lineTo(l - w * 2.6, w * 1.4); c.closePath(); c.fill(); c.restore();
|
|
192
|
+
};
|
|
193
|
+
const slab = (c, txt, x, y, ang, font, fg, bg, padX = 14, padY = 10, p = 1) => { // slanted bar with lettering
|
|
194
|
+
if (p <= 0) return; c.save(); c.translate(x, y); c.rotate(ang); c.font = font; c.letterSpacing = '3px';
|
|
195
|
+
const m = c.measureText(txt), w = m.width + padX * 2, h = (m.actualBoundingBoxAscent + m.actualBoundingBoxDescent) + padY * 2;
|
|
196
|
+
if (bg) { c.fillStyle = bg; c.fillRect(-padX, -m.actualBoundingBoxAscent - padY, w * p, h); }
|
|
197
|
+
if (p >= 1) { c.fillStyle = fg; c.fillText(txt, 0, 0); }
|
|
198
|
+
c.restore(); c.letterSpacing = '0px';
|
|
199
|
+
};
|
|
200
|
+
const cog = (c, x, y, r, teeth, a) => {
|
|
201
|
+
c.save(); c.translate(x, y); c.rotate(a); c.beginPath();
|
|
202
|
+
for (let i = 0; i < teeth * 2; i++) { const rr = i % 2 ? r : r * 1.22, a0 = (i / (teeth * 2)) * Math.PI * 2; c.lineTo(Math.cos(a0 - 0.12) * rr, Math.sin(a0 - 0.12) * rr); c.lineTo(Math.cos(a0 + 0.12) * rr, Math.sin(a0 + 0.12) * rr); }
|
|
203
|
+
c.closePath(); c.arc(0, 0, r * 0.38, 0, 7, true); c.fill(); c.restore();
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// worn-print finish: fine cream scratches through the ink (same seed in the still and the explainer)
|
|
207
|
+
const scratches = (ctx, W, HH) => {
|
|
208
|
+
const rs = H.rng(88);
|
|
209
|
+
ctx.save(); ctx.strokeStyle = 'rgba(250,244,228,0.35)'; ctx.lineWidth = 1;
|
|
210
|
+
for (let i = 0; i < 40; i++) { const x = rs.range(0, W), y = rs.range(0, HH), l = rs.range(4, 30), a2 = rs.range(0, 3); ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + Math.cos(a2) * l, y + Math.sin(a2) * l); ctx.stroke(); }
|
|
211
|
+
ctx.restore();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const explainConstructivist = (ctx, W, HH, t) => {
|
|
215
|
+
const E = H.ease, cl = H.clamp, S = (a, b) => cl((t - a) / (b - a), 0, 1);
|
|
216
|
+
const tq = Math.floor(t * 15) / 15; // agitprop staccato: punches land on twos
|
|
217
|
+
const Sq = (a, b) => cl((tq - a) / (b - a), 0, 1);
|
|
218
|
+
const WEDGES = [5.6, 10.0, 12.55], swapAt = (w) => w + 0.5;
|
|
219
|
+
const scene = t < swapAt(WEDGES[0]) ? 1 : t < swapAt(WEDGES[1]) ? 2 : t < swapAt(WEDGES[2]) ? 3 : 4;
|
|
220
|
+
const PH = exPhotos(W, HH);
|
|
221
|
+
ctx.drawImage(exPaperC(W, HH), 0, 0);
|
|
222
|
+
const L = H.layer(W, HH), c = L.getContext('2d'); c.textBaseline = 'alphabetic';
|
|
223
|
+
const pastes = []; // photos to paste after the flat plates
|
|
224
|
+
const R0 = { x: 14, y: 66, s: 0.85 }, bell = [14 + 690 * 0.85, 66 + 228 * 0.85];
|
|
225
|
+
|
|
226
|
+
// ---------- hook (scene 1 opening) ----------
|
|
227
|
+
const hp = E.inOut(S(1.6, 2.3));
|
|
228
|
+
if (hp < 1) {
|
|
229
|
+
c.save(); c.globalAlpha = 1;
|
|
230
|
+
const disc = E.out(Sq(0, 0.4)); c.fillStyle = RED; c.beginPath(); c.arc(980 - (1 - disc) * 600, 300, 230, 0, 7); c.fill();
|
|
231
|
+
const beams = [[-2.6, 0.06], [-2.3, 0.1], [-1.95, 0.04], [-1.7, 0.12]];
|
|
232
|
+
beams.forEach(([a, w], i) => { const p = Sq(0.2 + i * 0.08, 0.3 + i * 0.08); if (p <= 0) return; c.fillStyle = i % 2 ? RED : BLACK; c.beginPath(); c.moveTo(980, 300); c.lineTo(980 + Math.cos(a - w) * 1400 * p, 300 + Math.sin(a - w) * 1400 * p); c.lineTo(980 + Math.cos(a + w) * 1400 * p, 300 + Math.sin(a + w) * 1400 * p); c.closePath(); c.fill(); });
|
|
233
|
+
c.restore();
|
|
234
|
+
}
|
|
235
|
+
// the slogan collapses into a slanted corner banner
|
|
236
|
+
{
|
|
237
|
+
const x = H.lerp(110, 30, hp), y = H.lerp(430, 52, hp), s = H.lerp(1, 0.3, hp), ang = H.lerp(-0.2, -0.06, hp);
|
|
238
|
+
c.save(); c.translate(x, y); c.scale(s, s);
|
|
239
|
+
slab(c, 'HOW DOES', 0, 0, ang, `400 ${H.lerp(64, 96, hp).toFixed(2)}px "Russo One"`, CREAM, BLACK, 20, 16, Sq(0.35, 0.5));
|
|
240
|
+
if (tq >= 0.6) { c.save(); c.rotate(ang); const k = cl((tq - 0.6) / 0.2, 0, 1), sc = 1 + 0.6 * (1 - k); c.translate(0, 150); c.scale(sc, sc); c.fillStyle = RED; c.font = '400 170px "Anton"'; c.letterSpacing = '2px'; c.fillText('AI LEARN?', 0, 0); c.letterSpacing = '0px'; c.restore(); }
|
|
241
|
+
c.restore();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---------- scene 1: data poured into the megaphone ----------
|
|
245
|
+
if (scene === 1 && t > 2.0) {
|
|
246
|
+
const k = E.out(S(2.0, 2.6));
|
|
247
|
+
pastes.push(['robot', R0.x - (1 - k) * 500, R0.y + (1 - k) * 200, R0.s]);
|
|
248
|
+
// conveyor: heavy diagonal bar from upper right down to the bell
|
|
249
|
+
const cx0 = 1300, cy0 = 40, cx1 = bell[0] + 60, cy1 = bell[1] - 20;
|
|
250
|
+
c.save(); c.fillStyle = BLACK; c.translate(cx1, cy1); c.rotate(Math.atan2(cy0 - cy1, cx0 - cx1)); c.fillRect(0, 70, Math.hypot(cx0 - cx1, cy0 - cy1) * E.out(S(2.4, 2.8)), 26); c.restore();
|
|
251
|
+
// examples ride the bar into the bell (photos)
|
|
252
|
+
for (let i = 0; i < 9; i++) {
|
|
253
|
+
const u = (t - 2.7 - i * 0.42) * 0.55; if (u < 0 || u > 1) continue;
|
|
254
|
+
const kind = H.rng(i * 11 + 3)() < 0.5 ? 'cat' : 'dog', sh = 1 - E.in(cl((u - 0.75) / 0.25, 0, 1));
|
|
255
|
+
const x = H.lerp(cx0 - 60, cx1, u), y = H.lerp(cy0 + 40, cy1 + 20, u) - 80, s = 0.33 * sh;
|
|
256
|
+
pastes.push([kind + 'S', x - 300 * s, y - 300 * s, s, -0.25]);
|
|
257
|
+
if (sh > 0.6) slab(c, kind.toUpperCase(), x - 44, y + 72 * sh, -0.25, '700 28px "Oswald"', CREAM, RED, 8, 5); // label stamped on the card
|
|
258
|
+
}
|
|
259
|
+
// red arrow into the bell + label
|
|
260
|
+
barArrow(c, 1000, 420, bell[0] + 30, bell[1] + 40, E.out(Sq(3.0, 3.3)), 22, RED);
|
|
261
|
+
slab(c, 'TRAINING DATA', 840, 505, -0.24, '700 30px "Oswald"', CREAM, BLACK, 10, 6, Sq(3.2, 3.35));
|
|
262
|
+
// counter: stencil numerals in a slanted black box
|
|
263
|
+
const cp = S(3.0, 5.4), n = cp < 1 ? Math.pow(cp, 3) * 1e6 : 1e6;
|
|
264
|
+
c.save(); c.translate(640, 640); c.rotate(-0.12);
|
|
265
|
+
c.fillStyle = BLACK; c.fillRect(-20, -100, 600 * E.out(Sq(2.8, 3.0)), 118);
|
|
266
|
+
if (tq >= 3.0) { c.fillStyle = CREAM; c.font = '400 100px "Anton"'; c.letterSpacing = '4px'; c.fillText(Math.round(n).toLocaleString('en-US'), 0, 0); c.letterSpacing = '0px'; c.fillStyle = RED; c.font = '400 30px "Russo One"'; c.fillText('EXAMPLES', 440, 40); }
|
|
267
|
+
c.restore();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ---------- scene 2: guess -> check -> adjust ----------
|
|
271
|
+
if (scene === 2) {
|
|
272
|
+
const t0 = swapAt(WEDGES[0]);
|
|
273
|
+
pastes.push(['robot', R0.x, R0.y, R0.s]);
|
|
274
|
+
// new picture pinned top right
|
|
275
|
+
pastes.push(['catS', 760 - 300 * 0.28, 105 - 300 * 0.28 - (1 - E.out(Sq(t0, t0 + 0.3))) * 300, 0.28, 0.08]);
|
|
276
|
+
slab(c, 'NEW PICTURE', 470, 74, 0.08, '700 28px "Oswald"', CREAM, BLACK, 8, 5, Sq(t0 + 0.3, t0 + 0.45));
|
|
277
|
+
// the guess is shouted through the megaphone
|
|
278
|
+
const shout = (txt, ts, col) => {
|
|
279
|
+
const beams = [[-0.3, 0.1], [-0.08, 0.08], [0.16, 0.06]];
|
|
280
|
+
beams.forEach(([a, w], i) => { const p = cl(Math.floor((t - ts - i * 0.07) * 15) / 2, 0, 1); if (p <= 0) return; c.fillStyle = i % 2 ? BLACK : RED; c.beginPath(); c.moveTo(bell[0] + 20, bell[1]); c.lineTo(bell[0] + Math.cos(a - w) * 700 * p, bell[1] + Math.sin(a - w) * 700 * p); c.lineTo(bell[0] + Math.cos(a + w) * 700 * p, bell[1] + Math.sin(a + w) * 700 * p); c.closePath(); c.fill(); });
|
|
281
|
+
if (t > ts + 0.25) { c.save(); c.translate(720, 300); c.rotate(-0.2); const k = cl(Math.floor((t - ts - 0.25) * 15) / 3, 0, 1), sc = 1 + 0.5 * (1 - k); c.scale(sc, sc); c.font = '400 120px "Anton"'; c.fillStyle = BLACK; c.fillText(txt, 4, 4); c.fillStyle = col; c.fillText(txt, 0, 0); c.restore(); }
|
|
282
|
+
};
|
|
283
|
+
if (t < 9.3) {
|
|
284
|
+
shout('DOG?', 6.75, CREAM);
|
|
285
|
+
const xp = Sq(7.45, 7.6); // CHECK: a red cross is stamped over it
|
|
286
|
+
if (xp > 0) { c.save(); c.translate(1040, 220); c.fillStyle = RED; c.rotate(0.785); c.fillRect(-70 * xp, -14, 140 * xp, 28); c.fillRect(-14, -70 * xp, 28, 140 * xp); c.restore(); }
|
|
287
|
+
const rp = S(7.55, 7.85); if (rp > 0) { c.strokeStyle = BLACK; c.lineWidth = 12; c.beginPath(); c.ellipse(900, 250, 240, 120, -0.2, -2, -2 + Math.PI * 2 * rp); c.stroke(); }
|
|
288
|
+
if (tq > 7.7) slab(c, 'WRONG! IT IS A CAT', 700, 440, -0.2, '400 28px "Russo One"', CREAM, RED, 12, 8);
|
|
289
|
+
} else {
|
|
290
|
+
shout('CAT!', 9.35, RED);
|
|
291
|
+
const cp = Sq(9.7, 9.85); if (cp > 0) { c.save(); c.translate(1010, 210); c.rotate(-0.2); c.fillStyle = BLACK; c.fillRect(-16, -16, 30, 70 * cp); c.fillRect(-16, 38, 110 * cp, 30); c.restore(); }
|
|
292
|
+
}
|
|
293
|
+
// ADJUST: error bar flies back to the robot's gears; the cogs turn
|
|
294
|
+
barArrow(c, 760, 470, 390, 560, E.inOut(S(7.9, 8.35)), 20, RED);
|
|
295
|
+
if (t > 8.35 && t < 9.3) slab(c, 'ERROR', 545, 470, -0.24, '700 30px "Oswald"', CREAM, RED, 8, 5);
|
|
296
|
+
const turn = E.inOut(S(8.35, 9.2)) * 2.4 + (t > 9.3 ? (t - 9.3) * 0.8 : 0);
|
|
297
|
+
c.fillStyle = BLACK; cog(c, 300, 560, 40 * E.back(Sq(t0 + 0.2, t0 + 0.4)), 10, turn); cog(c, 368, 512, 26 * E.back(Sq(t0 + 0.3, t0 + 0.5)), 8, -turn * 1.5);
|
|
298
|
+
c.fillStyle = RED; cog(c, 248, 500, 20 * E.back(Sq(t0 + 0.35, t0 + 0.55)), 7, -turn * 2);
|
|
299
|
+
// the loop: three slanted bars wired into a circuit
|
|
300
|
+
const steps = [['GUESS', 760, 610], ['CHECK', 960, 560], ['ADJUST', 1110, 660]], act = t < 7.45 ? 0 : t < 8.3 ? 1 : t < 9.3 ? 2 : Math.floor((t - 9.3) * 3) % 3;
|
|
301
|
+
steps.forEach(([s, x, y], i) => slab(c, s, x, y, -0.2, '400 30px "Russo One"', CREAM, i === act && t > 6.7 ? RED : BLACK, 12, 8, Sq(t0 + 0.2 + i * 0.12, t0 + 0.32 + i * 0.12)));
|
|
302
|
+
barArrow(c, 890, 590, 950, 575, Sq(t0 + 0.6, t0 + 0.7), 10, BLACK);
|
|
303
|
+
barArrow(c, 1100, 575, 1150, 625, Sq(t0 + 0.7, t0 + 0.8), 10, BLACK);
|
|
304
|
+
barArrow(c, 1090, 680, 900, 640, Sq(t0 + 0.8, t0 + 0.9), 10, BLACK);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ---------- scene 3: progress ----------
|
|
308
|
+
if (scene === 3) {
|
|
309
|
+
const t0 = swapAt(WEDGES[1]), prog = E.inOut(S(t0 + 0.2, 12.0));
|
|
310
|
+
// emphasis: beams burst behind the final number
|
|
311
|
+
const burst = Sq(12.05, 12.25);
|
|
312
|
+
if (burst > 0) { for (let i = 0; i < 12; i++) { const a = i / 12 * Math.PI * 2 + 0.1; c.fillStyle = i % 2 ? RED : BLACK; c.beginPath(); c.moveTo(1010, 300); c.lineTo(1010 + Math.cos(a - 0.08) * 460 * burst, 300 + Math.sin(a - 0.08) * 460 * burst); c.lineTo(1010 + Math.cos(a + 0.08) * 460 * burst, 300 + Math.sin(a + 0.08) * 460 * burst); c.closePath(); c.fill(); } }
|
|
313
|
+
c.save(); c.translate(90, 640); c.rotate(-0.12);
|
|
314
|
+
c.fillStyle = BLACK; c.fillRect(0, 0, 700 * E.out(Sq(t0, t0 + 0.2)), 14);
|
|
315
|
+
for (let i = 0; i < 9; i++) { const acc = 61 + 36 * (1 - Math.pow(1 - i / 8, 2)), bp = E.out(cl(prog * 9 - i, 0, 1)), h = (acc - 45) * 7.5 * bp; c.fillStyle = i === 8 ? RED : i % 2 ? BLACK : RED; c.fillRect(20 + i * 76, -h, 56, h); }
|
|
316
|
+
slab(c, 'TRAINING ROUNDS →', 330, 62, 0, '700 30px "Oswald"', CREAM, BLACK, 8, 5, Sq(t0 + 0.2, t0 + 0.35));
|
|
317
|
+
c.restore();
|
|
318
|
+
c.save(); c.translate(820, 380); c.rotate(-0.12);
|
|
319
|
+
c.fillStyle = BLACK; c.fillRect(-20, -200, 420, 240);
|
|
320
|
+
c.fillStyle = burst > 0 ? RED : CREAM; c.font = '400 210px "Anton"'; c.fillText(Math.round(H.lerp(61, 97, prog)) + '%', 0, 0);
|
|
321
|
+
c.restore();
|
|
322
|
+
slab(c, 'ACCURACY', 820, 140, -0.12, '400 40px "Russo One"', CREAM, RED, 12, 8, Sq(t0 + 0.1, t0 + 0.25));
|
|
323
|
+
slab(c, '61% → 97%', 860, 505, -0.12, '700 36px "Oswald"', CREAM, RED, 12, 8, Sq(t0 + 0.4, t0 + 0.5));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ---------- scene 4: takeaway slogan ----------
|
|
327
|
+
if (scene === 4) {
|
|
328
|
+
const t0 = swapAt(WEDGES[2]);
|
|
329
|
+
c.fillStyle = RED; c.beginPath(); c.arc(1080, 560, 260, 0, 7); c.fill();
|
|
330
|
+
slab(c, 'MORE EXAMPLES', 90, 260, -0.15, '400 76px "Russo One"', CREAM, BLACK, 22, 16, Sq(t0, t0 + 0.15));
|
|
331
|
+
barArrow(c, 200, 320, 300, 455, E.out(Sq(t0 + 0.2, t0 + 0.4)), 28, RED);
|
|
332
|
+
if (tq >= t0 + 0.35) { c.save(); c.translate(320, 610); c.rotate(-0.15); const k = cl((tq - t0 - 0.35) / 0.2, 0, 1), sc = 1 + 0.5 * (1 - k); c.scale(sc, sc); c.font = '400 132px "Anton"'; c.fillStyle = RED; c.fillText('BETTER GUESSES', 7, 7); c.fillStyle = BLACK; c.fillText('BETTER GUESSES', 0, 0); c.restore(); }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ---------- Lissitzky's red wedge drives across between scenes ----------
|
|
336
|
+
const WL = H.layer(W, HH), wc = WL.getContext('2d'); let wedgeOn = false;
|
|
337
|
+
for (const w of WEDGES) {
|
|
338
|
+
const u = S(w, w + 0.95); if (u <= 0 || u >= 1) continue; wedgeOn = true; const c = wc;
|
|
339
|
+
const tx = H.lerp(-100, 3900, E.inOut(u));
|
|
340
|
+
c.fillStyle = RED; c.beginPath(); c.moveTo(tx, HH / 2); c.lineTo(tx - 2400, -1400); c.lineTo(tx - 2400, HH + 1400); c.closePath(); c.fill();
|
|
341
|
+
c.fillStyle = BLACK; c.beginPath(); c.moveTo(tx + 60, HH / 2 + 30); c.lineTo(tx - 2400, -1360); c.lineTo(tx - 2400, -1330); c.closePath(); c.fill();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ---------- print: plates multiply, then pasted photos, then grain ----------
|
|
345
|
+
exInk(L, 7, 0.26, 0.006);
|
|
346
|
+
const pasteAll = () => pastes.forEach(([k, x, y, s, rot]) => paste(ctx, PH[k], x, y, s, rot || 0));
|
|
347
|
+
// photos sit under the slogans/wedge: paste first, then plates on top (multiply keeps dots visible under ink)
|
|
348
|
+
pasteAll();
|
|
349
|
+
ctx.save(); ctx.globalCompositeOperation = 'multiply'; ctx.drawImage(L, 0, 0); ctx.restore();
|
|
350
|
+
if (wedgeOn) { exInk(WL, 7, 0.12, 0.004); ctx.drawImage(WL, 0, 0); }
|
|
351
|
+
scratches(ctx, W, HH);
|
|
352
|
+
H.grain(ctx, W, HH, 0.08, 4 + Math.floor(t * 12), true);
|
|
353
|
+
H.vignette(ctx, W, HH, 0.25, '50,30,10');
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
registerStyle({
|
|
357
|
+
id: 'constructivist',
|
|
358
|
+
name: 'Constructivist Propaganda',
|
|
359
|
+
category: 'Art & Design Movements',
|
|
360
|
+
description: 'A 1920s Rodchenko / El Lissitzky agitprop poster: red, black and cream only, a halftone photomontage robot bellowing through a megaphone, beams radiating from its voice, a red disc and hard diagonal bars, and heavy condensed lettering set on the slant — litho ink mottle on cheap paper.',
|
|
361
|
+
motion: 'Agitprop staccato: the red disc rolls in, the halftone robot slides up the diagonal, beams punch out of the megaphone two frames at a time, the slogan bars shoot across and the words slam down on the beat; in the hold the beam fan pulses with each shout.',
|
|
362
|
+
fonts: ['400 150px "Anton"', '400 40px "Russo One"', '700 18px "Oswald"'],
|
|
363
|
+
frameT: 4.6,
|
|
364
|
+
duration: 5,
|
|
365
|
+
readsPixels: true,
|
|
366
|
+
explainerDuration: 14.5,
|
|
367
|
+
explainerNote: "Agitprop sequence: Lissitzky's red wedge drives across the sheet between scenes, slogans and labels slam onto slanted bars on twos, arrows are heavy bars with wedge heads, the counter is stencil-tall Anton, and the key moments get a stamped red cross or a burst of beams.",
|
|
368
|
+
explainer(ctx, W, HH, t) { explainConstructivist(ctx, W, HH, t); },
|
|
369
|
+
// Sound: a print works on agitprop duty. Slogans and slabs slam down like a press, the red wedge is a heavy iron
|
|
370
|
+
// ram driving through with a low roar, the megaphone shouts are short brass blasts through a horn, beams crack out,
|
|
371
|
+
// the counter is stencil-stamped and the cogs ratchet. Times copied from explainConstructivist(); its slabs land
|
|
372
|
+
// "on twos" (t quantised to 1/15 s), so q(b) gives the first frame a slab or slogan is actually complete.
|
|
373
|
+
explainerSound(k) {
|
|
374
|
+
const D = 14.5, E = H.ease, cl = H.clamp;
|
|
375
|
+
const S = (t, a, b) => cl((t - a) / (b - a), 0, 1);
|
|
376
|
+
const q = (b) => Math.ceil(b * 15 - 1e-6) / 15;
|
|
377
|
+
const pan = (px) => cl((px / 1280) * 2 - 1, -1, 1) * 0.7;
|
|
378
|
+
const when = (a, b, ease, u) => { for (let i = 0; i <= 400; i++) if (ease(i / 400) >= u - 1e-6) return a + ((b - a) * i) / 400; return b; };
|
|
379
|
+
const WEDGES = [5.6, 10.0, 12.55], swapAt = (w) => w + 0.5;
|
|
380
|
+
// a slab or slogan slammed down: paper slap with a press thump (big = louder, heavier)
|
|
381
|
+
const slam = (t, g, p, minor) => { k.play('slap', t, { gain: 0.55 * g, pan: p, minor }); k.play('thud', t, { f: 110, gain: 0.5 * g, pan: p, minor }); };
|
|
382
|
+
// megaphone shout: detuned saws honking through a horn-shaped band
|
|
383
|
+
const horn = (t, notes, dur, g, p, minor) => notes.forEach((n, i) => { for (const det of [-9, 9])
|
|
384
|
+
k.tone(t, { f: n, type: 'sawtooth', detune: det, dur, gain: 0.035 * g, a: 0.02, hold: dur * 0.6, filter: 'bandpass', f2: [[0, 700], [0.05, 1500], [dur, 1000]], q: 0.9, pan: p + (i - (notes.length - 1) / 2) * 0.15, verb: 0.2, minor }); });
|
|
385
|
+
// a beam cracking out: short punchy noise burst
|
|
386
|
+
const beam = (t, g, p) => k.noise(t, { dur: 0.09, filter: 'bandpass', f: [[0, 600], [0.09, 1800]], q: 0.8, gain: 0.22 * g, a: 0.004, pan: p, minor: true });
|
|
387
|
+
// stencil-stamped counter digit
|
|
388
|
+
const numb = (t, g, p) => k.withMinor(() => { k.noise(t, { dur: 0.03, filter: 'bandpass', f: 1500, q: 1.2, gain: 0.32 * g, a: 0.001, pan: p }); k.tone(t, { f: 130, dur: 0.05, gain: 0.12 * g, pan: p }); });
|
|
389
|
+
// the red wedge: an iron ram driving left to right (inOut over 0.95 s, fastest at the middle)
|
|
390
|
+
const ram = (w) => {
|
|
391
|
+
k.noise(w, { dur: 0.95, color: 'brown', filter: 'lowpass', f: [[0, 150], [0.47, 900], [0.95, 200]], gain: 0.45, a: 0.4, pan: -0.3 });
|
|
392
|
+
k.noise(w + 0.2, { dur: 0.75, color: 'pink', filter: 'bandpass', f: [[0, 300], [0.27, 1300], [0.75, 400]], q: 0.8, gain: 0.16, a: 0.25, pan: 0.3 });
|
|
393
|
+
k.tone(w, { f: [[0, 45], [0.47, 72], [0.95, 40]], type: 'sawtooth', dur: 0.95, gain: 0.1, a: 0.4, filter: 'lowpass', f2: 300 });
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
k.bed('hum', 0, D, { gain: 0.35 }); // the print works
|
|
397
|
+
k.bed('room', 0, D, { gain: 0.4 });
|
|
398
|
+
|
|
399
|
+
// ---- hook: disc drives in (0-0.4), four beams crack out, HOW DOES slab, AI LEARN? slams (0.6 -> 0.8)
|
|
400
|
+
k.noise(0.0, { dur: 0.4, color: 'brown', filter: 'lowpass', f: [[0, 200], [0.4, 700]], gain: 0.3, a: 0.3, pan: 0.2 });
|
|
401
|
+
k.play('thud', q(0.4), { f: 90, gain: 0.6, pan: pan(980) });
|
|
402
|
+
[0, 1, 2, 3].forEach((i) => beam(q(0.2 + i * 0.08) + 1 / 15, 1, pan(700 - i * 100)));
|
|
403
|
+
slam(q(0.5), 0.8, -0.4, true);
|
|
404
|
+
k.play('stamp', 0.8, { gain: 0.65, pan: -0.2 }); k.play('impact', 0.8, { gain: 0.3, pan: -0.2 });
|
|
405
|
+
// slogan collapses into the corner banner (1.6-2.3): a soft sheet slide
|
|
406
|
+
k.noise(1.6, { dur: 0.7, color: 'pink', filter: 'bandpass', f: [[0, 400], [0.7, 900]], q: 0.7, gain: 0.08, a: 0.3, pan: -0.5, minor: true });
|
|
407
|
+
|
|
408
|
+
// ---- scene 1: DATA poured into the megaphone
|
|
409
|
+
k.noise(2.0, { dur: 0.5, color: 'brown', filter: 'lowpass', f: [[0, 250], [0.5, 600]], gain: 0.25, a: 0.15, pan: -0.5, minor: true }); // robot photo slides in
|
|
410
|
+
k.play('thud', when(2.0, 2.6, E.out, 0.95), { f: 100, gain: 0.4, pan: -0.5, minor: true });
|
|
411
|
+
k.noise(2.4, { dur: 0.4, color: 'brown', filter: 'bandpass', f: 350, q: 1.2, gain: 0.2, a: 0.05, pan: 0.4, minor: true }); // conveyor bar runs out
|
|
412
|
+
slam(3.0, 0.6, pan(900), true); // counter box set
|
|
413
|
+
const cards = []; for (let i = 0; 2.7 + i * 0.42 < swapAt(WEDGES[0]) - 0.1; i++) cards.push(2.7 + i * 0.42);
|
|
414
|
+
cards.forEach((tc) => k.play('stamp', tc, { gain: 0.3, pan: pan(1200), minor: true })); // each card arrives with its label stamped on
|
|
415
|
+
slam(q(3.3), 0.7, pan(820), true); // red arrow into the bell; TRAINING DATA lands one frame later on the same slam
|
|
416
|
+
// counter n = S(3.0,5.4)^3 * 1e6, stencil-stamped, speeding up, off the card stamps
|
|
417
|
+
for (let t = 3.12; t < 5.32; ) { const s = S(t, 3.0, 5.4); if (!cards.some((c) => t > c - 0.06 && t < c + 0.08)) numb(t, 0.7 + 0.4 * s, pan(900)); t += 0.16 - 0.075 * s; }
|
|
418
|
+
k.play('stamp', 5.4, { gain: 0.6, pan: pan(900) }); k.play('thud', 5.4, { f: 90, gain: 0.4, pan: pan(900) }); // 1,000,000
|
|
419
|
+
|
|
420
|
+
// ---- the red wedge drives across: 5.6, 10.0, 12.55
|
|
421
|
+
WEDGES.forEach(ram);
|
|
422
|
+
|
|
423
|
+
// ---- scene 2: GUESS / CHECK / ADJUST (t0 = 6.1)
|
|
424
|
+
const t0 = swapAt(WEDGES[0]);
|
|
425
|
+
slam(q(t0 + 0.3), 0.7, pan(680), true); // cat picture drops in
|
|
426
|
+
k.play('tock', q(t0 + 0.4), { f: 300, gain: 0.4, pan: pan(300), minor: true }); // cogs bolt on
|
|
427
|
+
slam(q(t0 + 0.45), 0.55, pan(800), true); // NEW PICTURE + CHECK slabs land on the same frame (GUESS/ADJUST ride along)
|
|
428
|
+
// the guess is shouted through the megaphone (beams 6.82+, DOG? slams at 7.2)
|
|
429
|
+
horn(6.82, ['D3', 'Ab3'], 0.38, 1, pan(800));
|
|
430
|
+
[0, 1, 2].forEach((i) => beam(6.75 + i * 0.07 + 1 / 15, 0.8, pan(800)));
|
|
431
|
+
slam(7.2, 0.9, pan(800), false);
|
|
432
|
+
// CHECK: red cross stamped on (7.47 -> 7.6), black ring brushed round it, WRONG slab
|
|
433
|
+
k.play('stamp', 7.6, { gain: 1.0, pan: pan(1040) });
|
|
434
|
+
horn(7.62, ['C3', 'Db3'], 0.45, 1.1, pan(1040));
|
|
435
|
+
k.play('stroke', 7.58, { material: 'brush', dur: 0.3, gain: 0.8, pan: pan(900), minor: true });
|
|
436
|
+
slam(q(7.71), 0.55, pan(800), true);
|
|
437
|
+
// ADJUST: the error bar drives back to the cogs (inOut 7.9-8.35), cogs ratchet round (inOut 8.35-9.2)
|
|
438
|
+
k.noise(7.9, { dur: 0.45, color: 'brown', filter: 'lowpass', f: [[0, 200], [0.25, 800], [0.45, 250]], gain: 0.3, a: 0.2, pan: -0.2 });
|
|
439
|
+
k.play('thud', 8.35, { f: 120, gain: 0.5, pan: pan(390) });
|
|
440
|
+
for (let i = 1, lt = 8.35; i <= 9; i++) { const t = when(8.35, 9.2, E.inOut, i / 9); if (t - lt < 0.09) continue; lt = t; k.play('tock', t, { f: 360 + (i % 2) * 70, gain: 0.45, pan: pan(300), minor: true }); k.play('click', t + 0.006, { f: 900, gain: 0.5, pan: pan(300), minor: true }); }
|
|
441
|
+
// the correction: CAT! shouted (9.35) on a bright major blast, slams at 9.8, check mark at 9.87
|
|
442
|
+
horn(9.42, ['C4', 'E4', 'G4'], 0.5, 1.1, pan(800));
|
|
443
|
+
[0, 1, 2].forEach((i) => beam(9.35 + i * 0.07 + 1 / 15, 0.8, pan(800)));
|
|
444
|
+
slam(9.8, 0.9, pan(800), false);
|
|
445
|
+
k.play('stamp', q(9.85), { gain: 0.5, pan: pan(1010) }); // check mark stamped
|
|
446
|
+
|
|
447
|
+
// ---- scene 3: PROGRESS (t0 = 10.5)
|
|
448
|
+
const t3 = swapAt(WEDGES[1]);
|
|
449
|
+
slam(q(t3 + 0.2), 0.5, -0.3, true); // base bar
|
|
450
|
+
slam(q(t3 + 0.25), 0.5, pan(820), true); // ACCURACY
|
|
451
|
+
// bars rise as prog = inOut(S(10.7,12.0)) passes i/9: a heavy chunk each (at least 100 ms apart), rising a little
|
|
452
|
+
const bars = []; for (let i = 1, lt = 0; i < 9; i++) { const t = when(t3 + 0.2, 12.0, E.inOut, i / 9); if (t - lt < 0.1) continue; lt = t; bars.push(t); k.play('thud', t, { f: 80 + i * 12, gain: 0.3, pan: pan(110 + i * 76), minor: true }); }
|
|
453
|
+
for (let t = t3 + 0.25, last = 61, lt = -1; t < 11.97; t += 1 / 120) {
|
|
454
|
+
const v = Math.round(H.lerp(61, 97, E.inOut(S(t, t3 + 0.2, 12.0))));
|
|
455
|
+
if (v !== last && t - lt >= 1 / 11 && !bars.some((b) => Math.abs(t - b) < 0.05)) { numb(t, 0.9, pan(1000)); last = v; lt = t; }
|
|
456
|
+
}
|
|
457
|
+
// 97%: beams burst behind the number (from 12.067), the biggest hit
|
|
458
|
+
const bt = q(12.05 + 1e-3);
|
|
459
|
+
k.play('impact', bt, { gain: 1.7, pan: pan(1010) }); k.play('thud', bt, { f: 70, gain: 0.9 });
|
|
460
|
+
k.play('stamp', bt, { gain: 0.9, pan: pan(1010) });
|
|
461
|
+
horn(bt + 0.01, ['C3', 'G3', 'C4', 'E4'], 0.7, 1.2, pan(1010));
|
|
462
|
+
|
|
463
|
+
// ---- scene 4: TAKEAWAY (t0 = 13.05): MORE EXAMPLES slab, arrow, BETTER GUESSES slams (13.4 -> 13.6)
|
|
464
|
+
const t4 = swapAt(WEDGES[2]);
|
|
465
|
+
slam(q(t4 + 0.15), 0.8, -0.4, false);
|
|
466
|
+
k.noise(t4 + 0.2, { dur: 0.25, color: 'brown', filter: 'lowpass', f: [[0, 250], [0.2, 700]], gain: 0.2, a: 0.1, pan: -0.3, minor: true });
|
|
467
|
+
k.play('stamp', 13.6, { gain: 0.6, pan: -0.1 }); k.play('thud', 13.6, { f: 80, gain: 0.35 });
|
|
468
|
+
horn(13.62, ['F3', 'A3', 'C4'], 0.25, 0.8, 0, true);
|
|
469
|
+
horn(13.9, ['C3', 'G3', 'C4', 'E4'], 0.55, 0.9, 0); // the resolve
|
|
470
|
+
},
|
|
471
|
+
draw(ctx, W, HH, t) {
|
|
472
|
+
const P = (d, dur = 0.5) => H.ease.out(H.clamp((t - d) / dur, 0, 1));
|
|
473
|
+
|
|
474
|
+
// ---------- paper ----------
|
|
475
|
+
ctx.drawImage(exPaperC(W, HH), 0, 0); // same paper maths as before, built once and cached
|
|
476
|
+
|
|
477
|
+
// ---------- flat ink plates ----------
|
|
478
|
+
const L = H.layer(W, HH), c = L.getContext('2d');
|
|
479
|
+
// megaphone geometry (needed for beams)
|
|
480
|
+
const mx1 = 690, my1 = 228;
|
|
481
|
+
// red disc behind the head
|
|
482
|
+
// disc rolls in from the left edge and lands
|
|
483
|
+
{ const p = H.ease.out(H.clamp(t / 0.7, 0, 1)); c.fillStyle = RED; c.beginPath(); c.arc(360 - (1 - p) * 640, 300, 235, 0, 7); c.fill(); }
|
|
484
|
+
// radiating beams from the bell
|
|
485
|
+
const beams = [[-0.62, 0.05, BLACK], [-0.5, 0.16, RED], [-0.28, 0.035, BLACK], [-0.19, 0.12, RED], [-0.02, 0.03, BLACK], [0.06, 0.07, RED], [0.18, 0.02, BLACK]];
|
|
486
|
+
beams.forEach(([ang, wid, col], i) => {
|
|
487
|
+
wid = wid;
|
|
488
|
+
// staccato: each beam punches out in two frames, then the whole fan "shouts" on a beat
|
|
489
|
+
const p = H.clamp(Math.floor((t - (1.0 + i * 0.1)) * 15) / 2, 0, 1), len = 1500 * p;
|
|
490
|
+
if (p <= 0) return;
|
|
491
|
+
const beat = t > 2.8 ? Math.exp(-((t - 2.8) % 0.5) * 9) : 0;
|
|
492
|
+
wid *= 1 + 0.22 * beat;
|
|
493
|
+
c.fillStyle = col; c.beginPath(); c.moveTo(mx1 + 20, my1);
|
|
494
|
+
c.lineTo(mx1 + Math.cos(ang - wid / 2) * len, my1 + Math.sin(ang - wid / 2) * len);
|
|
495
|
+
c.lineTo(mx1 + Math.cos(ang + wid / 2) * len, my1 + Math.sin(ang + wid / 2) * len); c.closePath(); c.fill();
|
|
496
|
+
});
|
|
497
|
+
// heavy diagonal bars
|
|
498
|
+
const ang = -0.24;
|
|
499
|
+
c.save(); c.translate(650, 580); c.rotate(ang);
|
|
500
|
+
c.fillStyle = BLACK; c.fillRect(-60, -54, 820 * P(1.55, 0.3), 92);
|
|
501
|
+
c.fillStyle = RED; c.fillRect(-700, 62, 1500 * P(1.45, 0.35), 22);
|
|
502
|
+
// lettering on the slant
|
|
503
|
+
c.fillStyle = CREAM; c.font = '400 38px "Russo One"'; c.letterSpacing = '5px';
|
|
504
|
+
if (t >= 1.95) c.fillText(t >= 2.1 ? 'BUILD THE' : 'BUILD', 6, 8); // words punched in on the beat
|
|
505
|
+
if (t >= 2.25) { // FUTURE slams down from oversize, stepped on twos
|
|
506
|
+
const k = H.clamp(Math.floor((t - 2.25) * 15) / 3, 0, 1), sc = 1 + 0.6 * (1 - k);
|
|
507
|
+
c.save(); c.translate(262 + 200, -10); c.scale(sc, sc); c.translate(-(262 + 200), 10);
|
|
508
|
+
c.fillStyle = RED; c.font = '400 132px "Anton"'; c.letterSpacing = '2px';
|
|
509
|
+
c.fillText('FUTURE', 262, 30); c.restore();
|
|
510
|
+
}
|
|
511
|
+
{ const txt = 'ALL POWER TO THE MACHINES · 2026', n = H.clamp(Math.floor((t - 2.55) * 40), 0, txt.length); // typed like a ticker
|
|
512
|
+
c.fillStyle = BLACK; c.font = '700 18px "Oswald"'; c.letterSpacing = '5px';
|
|
513
|
+
if (n > 0) c.fillText(txt.slice(0, n), 4, 118); }
|
|
514
|
+
c.restore();
|
|
515
|
+
// small black square + circle (Lissitzky accent)
|
|
516
|
+
c.letterSpacing = '0px';
|
|
517
|
+
exInk(L, 7, 0.26, 0.006);
|
|
518
|
+
|
|
519
|
+
// ---------- halftone photomontage ----------
|
|
520
|
+
// the photograph is a fixed object (cut out once, screened once) that then slides in as a whole
|
|
521
|
+
if (!PHOTO) PHOTO = screenPhoto((pc) => drawPhoto(pc, t), W, HH);
|
|
522
|
+
const { PH, HT, SIL } = PHOTO;
|
|
523
|
+
const slide = (1 - H.ease.out(H.clamp((t - 0.25) / 0.75, 0, 1))) * -420, ox = slide, oy = -slide * 0.4;
|
|
524
|
+
// composite: rim (paper-coloured under-layer, covering beams), then ink plates multiply, then halftone multiply
|
|
525
|
+
ctx.save(); ctx.globalCompositeOperation = 'multiply'; ctx.drawImage(L, 0, 0); ctx.restore();
|
|
526
|
+
ctx.save();
|
|
527
|
+
for (const [dx, dy] of [[-4, 0], [4, 0], [0, -4], [0, 4], [-3, -3], [3, 3], [3, -3], [-3, 3]]) ctx.drawImage(SIL, dx + ox, dy + oy);
|
|
528
|
+
ctx.drawImage(SIL, ox, oy);
|
|
529
|
+
ctx.restore();
|
|
530
|
+
// light tonal wash under the dots so the photo reads as a print on toned paper
|
|
531
|
+
ctx.save(); ctx.globalAlpha = 0.18; ctx.globalCompositeOperation = 'multiply'; ctx.drawImage(PH, ox, oy); ctx.restore();
|
|
532
|
+
ctx.save(); ctx.globalCompositeOperation = 'multiply'; ctx.drawImage(HT, 1.5 + ox, -1 + oy); ctx.restore();
|
|
533
|
+
|
|
534
|
+
// ---------- finish ----------
|
|
535
|
+
scratches(ctx, W, HH);
|
|
536
|
+
H.grain(ctx, W, HH, 0.08, 4 + Math.floor(t * 12), true);
|
|
537
|
+
H.vignette(ctx, W, HH, 0.25, '50,30,10');
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": 58,
|
|
3
|
+
"name": "Constructivist Propaganda",
|
|
4
|
+
"nameZh": "构成主义宣传画",
|
|
5
|
+
"family": "Art & Design Movements",
|
|
6
|
+
"familyZh": "艺术与设计流派",
|
|
7
|
+
"description": "A 1920s Rodchenko / El Lissitzky agitprop poster: red, black and cream only, a halftone photomontage robot bellowing through a megaphone, beams radiating from its voice, a red disc and hard diagonal bars, and heavy condensed lettering set on the slant — litho ink mottle on cheap paper.",
|
|
8
|
+
"motionNotes": "Motion: Agitprop staccato: the red disc rolls in, the halftone robot slides up the diagonal, beams punch out of the megaphone two frames at a time, the slogan bars shoot across and the words slam down on the beat; in the hold the beam fan pulses with each shout.",
|
|
9
|
+
"explainerNotes": "Explainer: Agitprop sequence: Lissitzky's red wedge drives across the sheet between scenes, slogans and labels slam onto slanted bars on twos, arrows are heavy bars with wedge heads, the counter is stencil-tall Anton, and the key moments get a stamped red cross or a burst of beams.",
|
|
10
|
+
"code": "code.js",
|
|
11
|
+
"registeredAs": "constructivist",
|
|
12
|
+
"source": "https://www.futurepedia.io/resources/ai-style-atlas/view"
|
|
13
|
+
}
|