Remotion: Krallen und Daumen als eigene Teile, weicherer Schal am Hals, Pinos Kopfgelenk, Noa im VerformTest (8 s)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
remotion/public/figuren/noa/einfluss/daumen_links.png
Normal file
|
After Width: | Height: | Size: 823 B |
BIN
remotion/public/figuren/noa/einfluss/daumen_rechts.png
Normal file
|
After Width: | Height: | Size: 712 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 601 B After Width: | Height: | Size: 577 B |
BIN
remotion/public/figuren/noa/teile/daumen_links.png
Normal file
|
After Width: | Height: | Size: 304 B |
BIN
remotion/public/figuren/noa/teile/daumen_rechts.png
Normal file
|
After Width: | Height: | Size: 270 B |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 361 B |
|
Before Width: | Height: | Size: 363 B After Width: | Height: | Size: 413 B |
|
Before Width: | Height: | Size: 806 B After Width: | Height: | Size: 792 B |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.1 KiB |
BIN
remotion/public/figuren/pino/einfluss/krallen_fuss_links.png
Normal file
|
After Width: | Height: | Size: 1,016 B |
BIN
remotion/public/figuren/pino/einfluss/krallen_fuss_rechts.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
remotion/public/figuren/pino/einfluss/krallen_hand_links.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
remotion/public/figuren/pino/einfluss/krallen_hand_rechts.png
Normal file
|
After Width: | Height: | Size: 980 B |
|
Before Width: | Height: | Size: 976 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 751 B After Width: | Height: | Size: 901 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 986 B |
|
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 950 B |
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
BIN
remotion/public/figuren/pino/teile/krallen_fuss_links.png
Normal file
|
After Width: | Height: | Size: 585 B |
BIN
remotion/public/figuren/pino/teile/krallen_fuss_rechts.png
Normal file
|
After Width: | Height: | Size: 639 B |
BIN
remotion/public/figuren/pino/teile/krallen_hand_links.png
Normal file
|
After Width: | Height: | Size: 600 B |
BIN
remotion/public/figuren/pino/teile/krallen_hand_rechts.png
Normal file
|
After Width: | Height: | Size: 545 B |
|
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 388 KiB |
|
|
@ -9,7 +9,7 @@ const BILD = {fps: 30, width: 1920, height: 1080} as const;
|
|||
export const Root: React.FC = () => (
|
||||
<>
|
||||
<Composition id="LaterneTest" component={LaterneTest} durationInFrames={420} {...BILD} />
|
||||
<Composition id="VerformTest" component={VerformTest} durationInFrames={180} {...BILD} />
|
||||
<Composition id="VerformTest" component={VerformTest} durationInFrames={240} {...BILD} />
|
||||
<Composition id="HintergrundAbend" component={HintergrundAbend} durationInFrames={1} {...BILD} />
|
||||
<Composition id="VordergrundAbend" component={VordergrundAbend} durationInFrames={1} {...BILD} />
|
||||
<Composition id="PapierStruktur" component={PapierStruktur} durationInFrames={1} {...BILD} />
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export const RIGS: Record<string, Record<string, RigTeil>> = {
|
|||
"eltern": "rumpf",
|
||||
"um": [
|
||||
124,
|
||||
244
|
||||
252
|
||||
]
|
||||
},
|
||||
"kopf": {
|
||||
|
|
@ -123,8 +123,8 @@ export const RIGS: Record<string, Record<string, RigTeil>> = {
|
|||
"kopf": {
|
||||
"eltern": "koerper",
|
||||
"um": [
|
||||
400,
|
||||
465
|
||||
405,
|
||||
450
|
||||
]
|
||||
},
|
||||
"ohr_links": {
|
||||
|
|
@ -176,6 +176,34 @@ export const RIGS: Record<string, Record<string, RigTeil>> = {
|
|||
600
|
||||
]
|
||||
},
|
||||
"krallen_hand_links": {
|
||||
"eltern": "arm_links",
|
||||
"um": [
|
||||
366,
|
||||
524
|
||||
]
|
||||
},
|
||||
"krallen_hand_rechts": {
|
||||
"eltern": "arm_rechts",
|
||||
"um": [
|
||||
452,
|
||||
526
|
||||
]
|
||||
},
|
||||
"krallen_fuss_links": {
|
||||
"eltern": "fuss_links",
|
||||
"um": [
|
||||
332,
|
||||
590
|
||||
]
|
||||
},
|
||||
"krallen_fuss_rechts": {
|
||||
"eltern": "fuss_rechts",
|
||||
"um": [
|
||||
526,
|
||||
580
|
||||
]
|
||||
},
|
||||
"bommel": {
|
||||
"eltern": "muetze",
|
||||
"um": [
|
||||
|
|
@ -234,6 +262,20 @@ export const RIGS: Record<string, Record<string, RigTeil>> = {
|
|||
172
|
||||
]
|
||||
},
|
||||
"daumen_links": {
|
||||
"eltern": "hand_links",
|
||||
"um": [
|
||||
68,
|
||||
300
|
||||
]
|
||||
},
|
||||
"daumen_rechts": {
|
||||
"eltern": "hand_rechts",
|
||||
"um": [
|
||||
186,
|
||||
296
|
||||
]
|
||||
},
|
||||
"bein_links": {
|
||||
"eltern": "rumpf",
|
||||
"um": [
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import React from 'react';
|
||||
import {AbsoluteFill, Img, staticFile, useCurrentFrame} from 'remotion';
|
||||
import {feder, kreis, kurve} from '../bewegung';
|
||||
import {LULU, PINO} from '../figuren';
|
||||
import {LULU, NOA, PINO} from '../figuren';
|
||||
import {RIGS} from '../rigs';
|
||||
import {VerformFigur, type Knochen} from '../teile/VerformFigur';
|
||||
|
||||
// Test für weiche Animation (6 s, ohne Ton): Lulu beugt den Hals zu Pino
|
||||
// hinunter, Pino freut sich und hüpft. Alles über Gitter-Verformung mit den
|
||||
// Test für weiche Animation (8 s, ohne Ton) mit möglichst vielen Bewegungsarten:
|
||||
// Lulu beugt den Hals, schüttelt den Kopf, stampft; Pino hüpft, winkt, tippt mit dem
|
||||
// Fuß; Noa winkt, wippt, hüpft, die Zöpfe schwingen nach. Alles über Gitter-Verformung mit den
|
||||
// Körperteil-Masken aus werkzeuge/rig.py und Federn; Hintergrund und Papier
|
||||
// sind vorberechnete Bilder.
|
||||
|
||||
|
|
@ -37,8 +38,9 @@ const ohrZucken = (f: number, bei: number[]) => (bei.some((b) => f >= b && f < b
|
|||
const luluKnochen = (f: number): Knochen[] => {
|
||||
const hals = feder(halsKurve, f, {steifheit: 0.11, daempfung: 0.2});
|
||||
const halsTraege = feder(halsKurve, f, {steifheit: 0.05, daempfung: 0.16});
|
||||
const kopf = feder((g) => -halsKurve(g) * 0.35 + neugierKurve(g), f, {steifheit: 0.07, daempfung: 0.18});
|
||||
const ohrL = feder((g) => -26 * ohrZucken(g, [20, 98]), f, {steifheit: 0.25, daempfung: 0.12}) + (hals - halsTraege) * 0.6;
|
||||
const schuetteln = (g: number) => (g > 160 && g < 200 ? 8 * Math.sin((g - 160) * 0.45) * Math.sin(((g - 160) / 40) * Math.PI) : 0);
|
||||
const kopf = feder((g) => -halsKurve(g) * 0.35 + neugierKurve(g) + schuetteln(g), f, {steifheit: 0.12, daempfung: 0.2});
|
||||
const ohrL = feder((g) => -26 * ohrZucken(g, [20, 98, 182]), f, {steifheit: 0.25, daempfung: 0.12}) + (hals - halsTraege) * 0.6;
|
||||
const ohrR = feder((g) => 22 * ohrZucken(g, [60, 104]), f, {steifheit: 0.25, daempfung: 0.12}) - (hals - halsTraege) * 0.5;
|
||||
const schwanz = -6 + 10 * Math.sin(f * 0.085) + feder((g) => halsKurve(g) * 0.5, f, {steifheit: 0.04, daempfung: 0.1}) - halsKurve(f) * 0.5;
|
||||
const schal = (halsTraege - hals) * 0.9 + 2.5 * Math.sin(f * 0.07);
|
||||
|
|
@ -48,7 +50,8 @@ const luluKnochen = (f: number): Knochen[] => {
|
|||
gelenk('lulu', 'ohr_rechts', ohrR),
|
||||
gelenk('lulu', 'kopf', kopf),
|
||||
gelenk('lulu', 'hals', hals),
|
||||
gelenk('lulu', 'schwanz', schwanz),
|
||||
gelenk('lulu', 'schwanz', schwanz + (f > 200 ? 8 * Math.sin(f * 0.3) : 0)),
|
||||
gelenk('lulu', 'bein_vorne_rechts', feder((g) => kurve(g, [[198, 0], [206, 14], [214, 0], [218, 6], [224, 0]]), f, {steifheit: 0.3, daempfung: 0.3})),
|
||||
gelenk('lulu', 'schal_ende', schal),
|
||||
{name: 'atmen', art: 'stauchen', um: [175, 330], sx: 1 - atmen * 0.006, sy: 1 + atmen * 0.012, gewicht: kreis(180, 300, 50, 110)},
|
||||
];
|
||||
|
|
@ -79,21 +82,54 @@ const pinoWerte = (f: number) => {
|
|||
const ohr = feder((g) => (g > SPRUNG.auf && g < SPRUNG.auf + 3 ? -14 : 0), f, {steifheit: 0.25, daempfung: 0.12});
|
||||
const atmen = Math.sin(f * 0.11);
|
||||
const knochen: Knochen[] = [
|
||||
gelenk('pino', 'krallen_fuss_rechts', f > 190 && f < 226 ? -8 * Math.sin((f - 190) * 0.7) : 0),
|
||||
gelenk('pino', 'krallen_hand_links', freude(f) * 10),
|
||||
gelenk('pino', 'krallen_hand_rechts', -freude(f) * 10),
|
||||
gelenk('pino', 'bommel', bommel + 3 * Math.sin(f * 0.06)),
|
||||
gelenk('pino', 'ohr_links', ohr),
|
||||
gelenk('pino', 'ohr_rechts', -ohr * 0.7),
|
||||
gelenk('pino', 'arm_links', freude(f) * 12),
|
||||
gelenk('pino', 'arm_rechts', -freude(f) * 10),
|
||||
gelenk('pino', 'kopf', feder(pinoKopfKurve, f, {steifheit: 0.08, daempfung: 0.2})),
|
||||
gelenk('pino', 'kopf', feder((g) => pinoKopfKurve(g) + (g > 150 && g < 176 ? 4 * Math.sin((g - 150) * 0.5) : 0), f, {steifheit: 0.1, daempfung: 0.2})),
|
||||
gelenk('pino', 'fuss_rechts', f > 190 && f < 226 ? -10 * Math.abs(Math.sin((f - 190) * 0.35)) : 0),
|
||||
{name: 'atmen', art: 'stauchen', um: [420, 600], sx: 1 + atmen * 0.01, sy: 1 + atmen * 0.014, gewicht: kreis(420, 540, 70, 170)},
|
||||
];
|
||||
return {stauch, hoch, knochen};
|
||||
};
|
||||
|
||||
// ---------- Noa ----------
|
||||
const noaKopfKurve = (f: number) => kurve(f, [[0, 0], [20, 0], [40, 8], [120, 8], [135, 0], [215, 0], [225, -5], [232, 4], [240, 0]]);
|
||||
const noaHuepfer = (f: number) => (f > 140 && f < 176 ? Math.abs(Math.sin(((f - 140) / 36) * Math.PI * 2)) : 0);
|
||||
const noaNeigen = (f: number) => kurve(f, [[0, 0], [180, 0], [192, 3], [204, -3], [216, 0]]);
|
||||
|
||||
const noaWerte = (f: number) => {
|
||||
const kopf = feder(noaKopfKurve, f, {steifheit: 0.1, daempfung: 0.2});
|
||||
const arm = feder((g) => kurve(g, [[0, 0], [58, 0], [74, -75], [118, -75], [134, 0]]), f, {steifheit: 0.15, daempfung: 0.25});
|
||||
const winken = f > 76 && f < 118 ? 18 * Math.sin((f - 76) * 0.45) : 0;
|
||||
const hoch = noaHuepfer(f) * 22;
|
||||
// Zöpfe hängen nach: reagieren auf Kopf, Neigen und Hüpfen
|
||||
const schwung = (g: number) => -noaKopfKurve(g) * 1.2 - noaNeigen(g) * 3 + noaHuepfer(g) * 10;
|
||||
const zopf = feder(schwung, f, {steifheit: 0.06, daempfung: 0.08}) + 2 * Math.sin(f * 0.07);
|
||||
const atmen = Math.sin(f * 0.1);
|
||||
const knochen: Knochen[] = [
|
||||
gelenk('noa', 'zopf_links', -zopf),
|
||||
gelenk('noa', 'zopf_rechts', zopf),
|
||||
gelenk('noa', 'daumen_rechts', winken * 0.8),
|
||||
gelenk('noa', 'hand_rechts', winken),
|
||||
gelenk('noa', 'arm_rechts', arm + winken * 0.3),
|
||||
gelenk('noa', 'arm_links', 3 * Math.sin(f * 0.06) + noaHuepfer(f) * 8),
|
||||
gelenk('noa', 'kopf', kopf),
|
||||
{name: 'atmen', art: 'stauchen', um: [115, 300], sx: 1 + atmen * 0.006, sy: 1 + atmen * 0.01, gewicht: kreis(115, 240, 40, 120)},
|
||||
];
|
||||
const stauch = feder((g) => (noaHuepfer(g) > 0 && noaHuepfer(g) < 0.15 ? 0.95 : 1), f, {steifheit: 0.3, daempfung: 0.3});
|
||||
return {knochen, hoch, stauch, neigen: feder(noaNeigen, f, {steifheit: 0.1, daempfung: 0.2})};
|
||||
};
|
||||
|
||||
export const VerformTest: React.FC = () => {
|
||||
const f = useCurrentFrame();
|
||||
const zoom = 1.04 - f * 0.0002;
|
||||
const pino = pinoWerte(f);
|
||||
const noa = noaWerte(f);
|
||||
const luluNeigen = kurve(f, [[30, 0], [74, -2.5], [118, -2.5], [150, 0]]) + Math.sin(f * 0.05) * 0.5;
|
||||
const abend = 'brightness(0.86) saturate(0.95)';
|
||||
|
||||
|
|
@ -101,6 +137,17 @@ export const VerformTest: React.FC = () => {
|
|||
<AbsoluteFill style={{backgroundColor: '#24325c', overflow: 'hidden'}}>
|
||||
<AbsoluteFill style={{transform: `scale(${zoom})`}}>
|
||||
<Img src={staticFile('hintergrund/abend.png')} style={{position: 'absolute', inset: 0, width: '100%', height: '100%'}} />
|
||||
<VerformFigur
|
||||
daten={NOA}
|
||||
hoehe={430}
|
||||
x={560}
|
||||
y={BODEN + 4 - noa.hoch}
|
||||
drehung={noa.neigen}
|
||||
stauchX={2 - noa.stauch}
|
||||
stauchY={noa.stauch}
|
||||
knochen={noa.knochen}
|
||||
filter={abend}
|
||||
/>
|
||||
<VerformFigur
|
||||
daten={PINO}
|
||||
hoehe={210}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ FIGUREN_DEF = {
|
|||
"knochen": [
|
||||
("rumpf", None, (130, 292), (232, 296), 0,
|
||||
[(120, 245), (170, 238), (230, 250), (248, 290), (245, 345), (200, 352), (150, 347), (105, 337), (95, 300)]),
|
||||
("hals", "rumpf", (124, 244), (112, 156), 14,
|
||||
("hals", "rumpf", (124, 252), (112, 156), 70,
|
||||
[(78, 150), (132, 140), (142, 178), (128, 205), (85, 205), (72, 176)]),
|
||||
("kopf", "hals", (111, 150), (86, 72), 10,
|
||||
[(10, 60), (40, 45), (60, 47), (80, 25), (110, 30), (140, 52), (146, 90), (136, 135), (126, 152),
|
||||
|
|
@ -84,7 +84,7 @@ FIGUREN_DEF = {
|
|||
"knochen": [
|
||||
("koerper", None, (230, 430), (400, 560), 0,
|
||||
[(20, 350), (150, 200), (245, 200), (262, 300), (275, 445), (300, 560), (200, 640), (60, 560)]),
|
||||
("kopf", "koerper", (400, 465), (420, 320), 26,
|
||||
("kopf", "koerper", (405, 450), (420, 320), 60,
|
||||
[(245, 150), (300, 115), (420, 120), (520, 180), (554, 240), (554, 455), (470, 465), (380, 462),
|
||||
(300, 450), (268, 400), (262, 300), (240, 240)]),
|
||||
("ohr_links", "kopf", (290, 258), (256, 222), 6, "kreis", (272, 240, 27)),
|
||||
|
|
@ -92,13 +92,21 @@ FIGUREN_DEF = {
|
|||
("bauch", "koerper", (330, 600), (430, 470), 20,
|
||||
[(300, 450), (470, 460), (485, 520), (480, 600), (470, 640), (400, 645), (360, 610), (300, 545)]),
|
||||
("arm_links", "koerper", (322, 462), (370, 540), 10,
|
||||
[(322, 456), (345, 470), (380, 505), (392, 540), (372, 556), (345, 548), (330, 515), (318, 480)]),
|
||||
[(322, 456), (345, 470), (382, 505), (400, 535), (398, 562), (372, 564), (345, 552), (330, 515), (318, 480)]),
|
||||
("arm_rechts", "koerper", (458, 468), (455, 545), 10,
|
||||
[(445, 465), (472, 470), (478, 520), (470, 556), (442, 556), (440, 510)]),
|
||||
[(445, 465), (472, 470), (478, 520), (474, 562), (440, 564), (424, 550), (422, 515), (430, 498), (442, 490)]),
|
||||
("fuss_links", "koerper", (310, 568), (320, 645), 10,
|
||||
[(305, 565), (355, 563), (360, 600), (350, 655), (315, 658), (305, 620)]),
|
||||
[(300, 560), (360, 556), (364, 600), (350, 655), (315, 658), (300, 620)]),
|
||||
("fuss_rechts", "koerper", (495, 600), (540, 575), 10,
|
||||
[(495, 560), (540, 548), (554, 575), (545, 640), (505, 640), (490, 600)]),
|
||||
[(495, 556), (540, 540), (554, 560), (554, 640), (505, 640), (490, 600)]),
|
||||
("krallen_hand_links", "arm_links", (366, 524), (372, 540), 4,
|
||||
[(348, 524), (366, 514), (395, 520), (395, 546), (352, 548)]),
|
||||
("krallen_hand_rechts", "arm_rechts", (452, 526), (454, 542), 4,
|
||||
[(434, 528), (455, 522), (474, 528), (472, 546), (436, 546)]),
|
||||
("krallen_fuss_links", "fuss_links", (332, 590), (332, 568), 4,
|
||||
[(306, 566), (330, 558), (358, 562), (356, 594), (308, 594)]),
|
||||
("krallen_fuss_rechts", "fuss_rechts", (526, 580), (540, 556), 4,
|
||||
[(512, 560), (532, 546), (554, 548), (554, 578), (516, 582)]),
|
||||
],
|
||||
"accessoires": [
|
||||
{
|
||||
|
|
@ -128,6 +136,10 @@ FIGUREN_DEF = {
|
|||
[(35, 180), (62, 165), (78, 180), (80, 290), (60, 300), (35, 295), (28, 240)]),
|
||||
("arm_rechts", "rumpf", (166, 172), (183, 272), 12,
|
||||
[(160, 170), (180, 180), (192, 230), (195, 275), (178, 280), (165, 240), (158, 190)]),
|
||||
("daumen_links", "hand_links", (68, 300), (78, 310), 3,
|
||||
[(67, 292), (76, 294), (81, 304), (79, 315), (70, 315), (66, 302)]),
|
||||
("daumen_rechts", "hand_rechts", (186, 296), (192, 281), 3,
|
||||
[(184, 279), (192, 278), (199, 286), (198, 298), (188, 299), (183, 290)]),
|
||||
("bein_links", "rumpf", (92, 322), (92, 350), 6,
|
||||
[(78, 320), (108, 320), (108, 352), (80, 352)]),
|
||||
("bein_rechts", "rumpf", (128, 322), (132, 350), 6,
|
||||
|
|
@ -291,6 +303,12 @@ def baue(figur, d):
|
|||
labels[rest] = koerper[rest]
|
||||
# 3. abgeschnittene Inseln über die ganze Figur auffüllen
|
||||
labels = ausbreiten(labels, figur_maske)
|
||||
# 4. Teile, die an einem Accessoire hängen (z. B. Daumen am Fäustling), nehmen sich ihre Fläche daraus
|
||||
accessoire_namen = {t[0] for g in d["accessoires"] for t in g["teile"]}
|
||||
for name, eltern, _, _, _, *flaeche in d["knochen"]:
|
||||
if eltern in accessoire_namen and flaeche:
|
||||
f = flaechen_pixel(flaeche, form) & figur_maske & (labels == nummer[eltern])
|
||||
labels[f] = nummer[name]
|
||||
|
||||
ziel = os.path.join(FIGUREN, figur)
|
||||
os.makedirs(os.path.join(ziel, "teile"), exist_ok=True)
|
||||
|
|
|
|||