[READ-ONLY] Mirror of https://github.com/flo-bit/tiny-creature. three.js journey #16 - tamagotchi flo-bit.dev/tiny-creature/
0

Configure Feed

Select the types of activity you want to include in your feed.

update mouth

Florian (Feb 14, 2025, 1:42 AM +0100) 810f966e 9eab8a73

+600 -43
+4 -7
src/lib/Creature.svelte
··· 48 48 let face: Awaited<ReturnType<typeof createFace>>; 49 49 50 50 async function createTextures() { 51 + let s = 1024; 51 52 face = await createFace({ 52 - width: 256, 53 - height: 256, 54 - scale: 0.25 53 + width: s, 54 + height: s, 55 + scale: s / 1024 55 56 }); 56 57 57 58 faceTexture = new THREE.CanvasTexture(face.faceCanvas); ··· 264 265 265 266 face.rightEye.options.setMultiple({ 266 267 ...options.rightEye, 267 - }); 268 - 269 - face.furMouth.options.setMultiple({ 270 - ...options.mouth, 271 268 }); 272 269 273 270 face.updateBackground(options.colors.primary);
+61 -4
src/lib/Pane.svelte
··· 202 202 </Folder> 203 203 <Folder expanded={false} title="Mouth"> 204 204 <Slider 205 - label="curve" 206 - bind:value={options.mouth.curve} 205 + label="lowerLipCx1" 206 + bind:value={options.mouth.lowerLipCx1} 207 + min={-100} 208 + max={100} 209 + on:change={change} 210 + /> 211 + <Slider 212 + label="lowerLipCy1" 213 + bind:value={options.mouth.lowerLipCy1} 214 + min={-100} 215 + max={100} 216 + on:change={change} 217 + /> 218 + <Slider 219 + label="lowerLipCx2" 220 + bind:value={options.mouth.lowerLipCx2} 221 + min={-100} 222 + max={100} 223 + on:change={change} 224 + /> 225 + <Slider 226 + label="lowerLipCy2" 227 + bind:value={options.mouth.lowerLipCy2} 228 + min={-100} 229 + max={100} 230 + on:change={change} 231 + /> 232 + <Slider 233 + label="lowerLipStroke" 234 + bind:value={options.mouth.lowerLipStroke} 235 + min={1} 236 + max={200} 237 + on:change={change} 238 + /> 239 + <Slider 240 + label="upperLipCx1" 241 + bind:value={options.mouth.upperLipCx1} 207 242 min={-100} 208 243 max={100} 209 244 on:change={change} 210 245 /> 211 246 <Slider 212 - label="stroke" 213 - bind:value={options.mouth.stroke} 247 + label="upperLipCy1" 248 + bind:value={options.mouth.upperLipCy1} 249 + min={-100} 250 + max={100} 251 + on:change={change} 252 + /> 253 + <Slider 254 + label="upperLipCx2" 255 + bind:value={options.mouth.upperLipCx2} 256 + min={-100} 257 + max={100} 258 + on:change={change} 259 + /> 260 + <Slider 261 + label="upperLipCy2" 262 + bind:value={options.mouth.upperLipCy2} 263 + min={-100} 264 + max={100} 265 + on:change={change} 266 + /> 267 + <Slider 268 + label="upperLipStroke" 269 + bind:value={options.mouth.upperLipStroke} 214 270 min={1} 215 271 max={200} 216 272 on:change={change} 217 273 /> 274 + 218 275 <Slider label="width" bind:value={options.mouth.width} min={1} max={200} on:change={change} /> 219 276 <Slider label="x" bind:value={options.mouth.x} min={-100} max={100} on:change={change} /> 220 277 <Slider label="y" bind:value={options.mouth.y} min={-200} max={100} on:change={change} />
+29 -11
src/lib/face/face.ts
··· 2 2 import Eye from './eye'; 3 3 import Curve from './curve'; 4 4 import { options } from '$lib/state.svelte'; 5 + import Mouth from './mouth'; 5 6 6 7 export async function createFace({ 7 8 width = 512, ··· 19 20 updateBackground: (color: string, alpha?: number) => void; 20 21 leftEye: Eye; 21 22 rightEye: Eye; 22 - mouth: Curve; 23 + mouth: Mouth; 23 24 eyebrowLeft: Curve; 24 25 eyebrowRight: Curve; 25 - furMouth: Curve; 26 26 }> { 27 27 const xScale = 0.45 * scale; 28 28 const yScale = 1.5 * scale; ··· 34 34 background: 'black' 35 35 }); 36 36 37 + // add to dom 38 + document.body.appendChild(faceApp.canvas); 39 + // set to fixed position 40 + faceApp.canvas.style.position = 'fixed'; 41 + faceApp.canvas.style.top = '0'; 42 + faceApp.canvas.style.left = '0'; 43 + faceApp.canvas.style.width = '100%'; 44 + faceApp.canvas.style.height = '100%'; 45 + 46 + faceApp.canvas.style.display = 'none'; 47 + 48 + // set y scale of dom to -1 49 + faceApp.canvas.style.transform = 'scaleY(-1)'; 50 + 51 + // show hide with key c 52 + let visible = false; 53 + window.addEventListener('keydown', (e) => { 54 + if (e.key === 'c') { 55 + visible = !visible; 56 + faceApp.canvas.style.display = visible ? 'block' : 'none'; 57 + } 58 + }); 59 + 37 60 const background = new Graphics(); 38 61 background.rect(0, 0, width, height); 39 62 background.fill({ color: '#be185d' }); ··· 54 77 container.addChild(leftEye.container); 55 78 56 79 const mouthColor = new Color(options.mouth.color); 57 - const mouth = new Curve({ 80 + const mouth = new Mouth({ 58 81 ...options.mouth, 59 82 colorRed: mouthColor.red * 256, 60 83 colorGreen: mouthColor.green * 256, 61 84 colorBlue: mouthColor.blue * 256 62 85 }); 63 - container.addChild(mouth.graphics); 86 + container.addChild(mouth.container); 64 87 65 88 const furApp = new Application(); 66 89 ··· 110 133 eyebrowRight.graphics.alpha = 0.5; 111 134 furContainer.addChild(eyebrowRight.graphics); 112 135 113 - const furMouth = new Curve(options.mouth); 114 - 115 - furMouth.graphics.blendMode = 'erase'; 116 - furContainer.addChild(furMouth.graphics); 136 + furContainer.addChild(mouth.furMask); 117 137 118 138 return { 119 139 faceCanvas: faceApp.canvas, ··· 127 147 eyebrowRight.update(dt); 128 148 129 149 mouth.update(dt); 130 - furMouth.update(dt); 131 150 132 151 leftEye.update(dt); 133 152 rightEye.update(dt); ··· 144 163 rightEye, 145 164 mouth, 146 165 eyebrowLeft, 147 - eyebrowRight, 148 - furMouth 166 + eyebrowRight 149 167 }; 150 168 }
+325
src/lib/face/mouth.ts
··· 1 + import * as PIXI from 'pixi.js'; 2 + import { AnimatedProperties } from './math-helper'; 3 + 4 + export type CurveOptions = { 5 + scale: number; 6 + 7 + upperLipStroke: number; 8 + upperLipCurve: number; 9 + 10 + upperLipCx1: number; 11 + upperLipCy1: number; 12 + upperLipCx2: number; 13 + upperLipCy2: number; 14 + 15 + lowerLipStroke: number; 16 + lowerLipCurve: number; 17 + 18 + lowerLipCx1: number; 19 + lowerLipCy1: number; 20 + lowerLipCx2: number; 21 + lowerLipCy2: number; 22 + 23 + width: number; 24 + 25 + colorRed: number; 26 + colorGreen: number; 27 + colorBlue: number; 28 + 29 + x: number; 30 + y: number; 31 + 32 + angle: number; 33 + sharpness: number; 34 + }; 35 + 36 + export default class Mouth { 37 + container: PIXI.Container; 38 + 39 + graphics: PIXI.Graphics; 40 + lips: PIXI.Graphics; 41 + // teeth: PIXI.Graphics; 42 + // teethMask: PIXI.Graphics; 43 + 44 + furMask: PIXI.Graphics; 45 + 46 + options: AnimatedProperties<CurveOptions>; 47 + 48 + constructor(opts: Partial<CurveOptions>) { 49 + this.options = new AnimatedProperties({ 50 + scale: opts?.scale ?? 1, 51 + upperLipStroke: opts?.upperLipStroke ?? 5, 52 + upperLipCurve: opts?.upperLipCurve ?? 10, 53 + 54 + upperLipCx1: opts?.upperLipCx1 ?? 0, 55 + upperLipCy1: opts?.upperLipCy1 ?? 0, 56 + upperLipCx2: opts?.upperLipCx2 ?? 0, 57 + upperLipCy2: opts?.upperLipCy2 ?? 0, 58 + 59 + lowerLipStroke: opts?.lowerLipStroke ?? 5, 60 + lowerLipCurve: opts?.lowerLipCurve ?? 10, 61 + 62 + lowerLipCx1: opts?.lowerLipCx1 ?? 0, 63 + lowerLipCy1: opts?.lowerLipCy1 ?? 0, 64 + lowerLipCx2: opts?.lowerLipCx2 ?? 0, 65 + lowerLipCy2: opts?.lowerLipCy2 ?? 0, 66 + 67 + width: opts?.width ?? 50, 68 + 69 + x: opts?.x ?? 0, 70 + y: opts?.y ?? 0, 71 + angle: opts?.angle ?? 0, 72 + sharpness: opts?.sharpness ?? 0, 73 + colorRed: opts?.colorRed ?? 20, 74 + colorGreen: opts?.colorGreen ?? 20, 75 + colorBlue: opts?.colorBlue ?? 20 76 + }); 77 + 78 + this.container = new PIXI.Container(); 79 + 80 + this.lips = new PIXI.Graphics(); 81 + this.container.addChild(this.lips); 82 + 83 + this.graphics = new PIXI.Graphics(); 84 + this.container.addChild(this.graphics); 85 + 86 + // this.teethMask = new PIXI.Graphics(); 87 + // this.teeth = new PIXI.Graphics(); 88 + // this.teeth.addChild(this.teethMask); 89 + // // this.teeth.mask = this.teethMask; 90 + // this.container.addChild(this.teeth); 91 + // this.container.addChild(this.teethMask); 92 + 93 + this.furMask = new PIXI.Graphics(); 94 + this.furMask.blendMode = 'erase'; 95 + 96 + this.draw(); 97 + } 98 + 99 + draw() { 100 + const upperLipStroke = this.options.get('upperLipStroke'); 101 + 102 + const upperLipCx1 = this.options.get('upperLipCx1'); 103 + const upperLipCy1 = this.options.get('upperLipCy1'); 104 + const upperLipCx2 = this.options.get('upperLipCx2'); 105 + const upperLipCy2 = this.options.get('upperLipCy2'); 106 + 107 + const lowerLipStroke = this.options.get('lowerLipStroke'); 108 + 109 + const lowerLipCx1 = this.options.get('lowerLipCx1'); 110 + const lowerLipCy1 = this.options.get('lowerLipCy1'); 111 + const lowerLipCx2 = this.options.get('lowerLipCx2'); 112 + const lowerLipCy2 = this.options.get('lowerLipCy2'); 113 + 114 + const mouthWidth = this.options.get('width'); 115 + const scale = this.options.get('scale'); 116 + const x = this.options.get('x'); 117 + const y = this.options.get('y'); 118 + const angle = this.options.get('angle'); 119 + const color = new PIXI.Color({ 120 + r: this.options.get('colorRed'), 121 + g: this.options.get('colorGreen'), 122 + b: this.options.get('colorBlue') 123 + }); 124 + 125 + this.graphics.clear(); 126 + this.graphics 127 + .moveTo((-mouthWidth / 2) * scale, 0) 128 + .bezierCurveTo( 129 + upperLipCx1 * scale, 130 + upperLipCy1 * scale, 131 + upperLipCx2 * scale, 132 + upperLipCy2 * scale, 133 + (mouthWidth / 2) * scale, 134 + 0 135 + ) 136 + .bezierCurveTo( 137 + lowerLipCx1 * scale, 138 + lowerLipCy1 * scale, 139 + lowerLipCx2 * scale, 140 + lowerLipCy2 * scale, 141 + -(mouthWidth / 2) * scale, 142 + 0 143 + ) 144 + .bezierCurveTo( 145 + upperLipCx1 * scale, 146 + upperLipCy1 * scale, 147 + upperLipCx2 * scale, 148 + upperLipCy2 * scale, 149 + (mouthWidth / 2) * scale, 150 + 0 151 + ) 152 + .bezierCurveTo( 153 + lowerLipCx1 * scale, 154 + lowerLipCy1 * scale, 155 + lowerLipCx2 * scale, 156 + lowerLipCy2 * scale, 157 + -(mouthWidth / 2) * scale, 158 + 0 159 + ); 160 + 161 + this.graphics.fill({ 162 + color: 0x220000 163 + }); 164 + 165 + // this.teeth.clear(); 166 + // this.teeth 167 + // .moveTo((-mouthWidth / 2) * scale, 0) 168 + // .quadraticCurveTo(0, upperLipCurve * scale, (mouthWidth / 2) * scale, 0) 169 + // .bezierCurveTo( 170 + // mouthWidth / 2, 171 + // lowerLipCurve * scale * 0.5, 172 + // -mouthWidth / 2, 173 + // lowerLipCurve * scale * 0.5, 174 + // -(mouthWidth / 2) * scale, 175 + // 0 176 + // ) 177 + // .quadraticCurveTo(0, upperLipCurve * scale, (mouthWidth / 2) * scale, 0); 178 + 179 + // this.teethMask.clear(); 180 + // for (let i = 0; i < 11; i++) { 181 + // // how close to center 182 + // const distanceToCenter = 1 - Math.abs(i - 5.5) / 5.5; 183 + 184 + // this.teethMask.rect( 185 + // (-mouthWidth / 2) * scale + (i * mouthWidth) / 10, 186 + // -50, 187 + // 15 * distanceToCenter, 188 + // 50 189 + // ); 190 + // } 191 + // this.teethMask.fill({ 192 + // color: 0xffffff 193 + // }); 194 + 195 + // const colorStops = [0xa1a1a1, 0xffffff, 0xffffff, 0xa1a1a1]; 196 + 197 + // const gradient = new PIXI.FillGradient( 198 + // (-mouthWidth / 2) * scale, 199 + // 0, 200 + // (mouthWidth / 2) * scale, 201 + // 0 202 + // ); 203 + 204 + // colorStops.forEach((number, index) => { 205 + // const ratio = index / colorStops.length; 206 + 207 + // gradient.addColorStop(ratio, number); 208 + // }); 209 + // this.teeth.fill(gradient); 210 + 211 + this.lips.clear(); 212 + this.lips 213 + .moveTo((-mouthWidth / 2) * scale, 0) 214 + .bezierCurveTo( 215 + upperLipCx1 * scale, 216 + upperLipCy1 * scale, 217 + upperLipCx2 * scale, 218 + upperLipCy2 * scale, 219 + (mouthWidth / 2) * scale, 220 + 0 221 + ) 222 + .stroke({ 223 + color: color, 224 + width: upperLipStroke * scale, 225 + cap: 'round' 226 + }); 227 + 228 + this.lips 229 + .bezierCurveTo( 230 + lowerLipCx1 * scale, 231 + lowerLipCy1 * scale, 232 + lowerLipCx2 * scale, 233 + lowerLipCy2 * scale, 234 + -(mouthWidth / 2) * scale, 235 + 0 236 + ) 237 + .stroke({ 238 + color: color, 239 + width: lowerLipStroke * scale, 240 + cap: 'round' 241 + }); 242 + 243 + this.furMask.clear(); 244 + this.furMask 245 + .moveTo((-mouthWidth / 2) * scale, 0) 246 + .bezierCurveTo( 247 + upperLipCx1 * scale, 248 + upperLipCy1 * scale, 249 + upperLipCx2 * scale, 250 + upperLipCy2 * scale, 251 + (mouthWidth / 2) * scale, 252 + 0 253 + ) 254 + .bezierCurveTo( 255 + lowerLipCx1 * scale, 256 + lowerLipCy1 * scale, 257 + lowerLipCx2 * scale, 258 + lowerLipCy2 * scale, 259 + -(mouthWidth / 2) * scale, 260 + 0 261 + ) 262 + .bezierCurveTo( 263 + upperLipCx1 * scale, 264 + upperLipCy1 * scale, 265 + upperLipCx2 * scale, 266 + upperLipCy2 * scale, 267 + (mouthWidth / 2) * scale, 268 + 0 269 + ) 270 + .bezierCurveTo( 271 + lowerLipCx1 * scale, 272 + lowerLipCy1 * scale, 273 + lowerLipCx2 * scale, 274 + lowerLipCy2 * scale, 275 + -(mouthWidth / 2) * scale, 276 + 0 277 + ); 278 + 279 + this.furMask.fill({ 280 + color: 0xffffff 281 + }); 282 + 283 + this.furMask 284 + .moveTo((-mouthWidth / 2) * scale, 0) 285 + .bezierCurveTo( 286 + upperLipCx1 * scale, 287 + upperLipCy1 * scale, 288 + upperLipCx2 * scale, 289 + upperLipCy2 * scale, 290 + (mouthWidth / 2) * scale, 291 + 0 292 + ) 293 + .stroke({ 294 + color: 0xffffff, 295 + width: upperLipStroke * scale, 296 + cap: 'round' 297 + }); 298 + 299 + this.furMask 300 + .bezierCurveTo( 301 + lowerLipCx1 * scale, 302 + lowerLipCy1 * scale, 303 + lowerLipCx2 * scale, 304 + lowerLipCy2 * scale, 305 + -(mouthWidth / 2) * scale, 306 + 0 307 + ) 308 + .stroke({ 309 + color: 0xffffff, 310 + width: lowerLipStroke * scale, 311 + cap: 'round' 312 + }); 313 + 314 + this.container.angle = angle; 315 + this.container.position.set(x * scale, y * scale); 316 + 317 + this.furMask.angle = angle; 318 + this.furMask.position.set(x * scale, y * scale); 319 + } 320 + 321 + update(deltaTime: number) { 322 + const updated = this.options.update(deltaTime); 323 + if (updated) this.draw(); 324 + } 325 + }
+181 -21
src/lib/state.svelte.ts
··· 27 27 stroke: 20, 28 28 color: '#000000', 29 29 width: 100, 30 - curve: -40 30 + curve: -40, 31 + upperLipStroke: 30, 32 + 33 + upperLipCx1: -30, 34 + upperLipCy1: -20, 35 + upperLipCx2: 30, 36 + upperLipCy2: -20, 37 + 38 + lowerLipStroke: 30, 39 + 40 + lowerLipCx1: 30, 41 + lowerLipCy1: -40, 42 + lowerLipCx2: -30, 43 + lowerLipCy2: -40, 44 + 45 + mouthWidth: 200 31 46 }, 32 47 leftEye: { 33 48 x: 75, ··· 70 85 sharpness: 0, 71 86 color: '#515151' 72 87 }, 73 - mouth: { x: 0, y: -130, stroke: 20, color: '#000000', width: 100, curve: -40 }, 74 - leftEye: { x: 75, y: 0, sizeX: 50, sizeY: 50, pupilSizeX: 20, pupilSizeY: 20 }, 75 - rightEye: { x: -75, y: 0, sizeX: 50, sizeY: 50, pupilSizeX: 20, pupilSizeY: 20 } 88 + mouth: { 89 + x: 0, 90 + y: -130, 91 + stroke: 20, 92 + color: '#000000', 93 + width: 100, 94 + curve: -40, 95 + upperLipStroke: 30, 96 + upperLipCx1: -30, 97 + upperLipCy1: -20, 98 + upperLipCx2: 30, 99 + upperLipCy2: -20, 100 + lowerLipStroke: 30, 101 + lowerLipCx1: 30, 102 + lowerLipCy1: -40, 103 + lowerLipCx2: -30, 104 + lowerLipCy2: -40, 105 + mouthWidth: 200 106 + }, 107 + leftEye: { x: 75, y: 0, sizeX: 60, sizeY: 60, pupilSizeX: 25, pupilSizeY: 25 }, 108 + rightEye: { x: -75, y: 0, sizeX: 60, sizeY: 60, pupilSizeX: 25, pupilSizeY: 25 } 76 109 }, 77 110 scared: { 78 111 leftEyebrow: { ··· 95 128 sharpness: 0, 96 129 color: '#515151' 97 130 }, 98 - mouth: { x: 0, y: -130, stroke: 30, color: '#000000', width: 150, curve: 40 }, 99 - leftEye: { x: 75, y: 0, sizeX: 50, sizeY: 40, pupilSizeX: 20, pupilSizeY: 20 }, 100 - rightEye: { x: -75, y: 0, sizeX: 50, sizeY: 40, pupilSizeX: 20, pupilSizeY: 20 } 131 + mouth: { 132 + x: 0, 133 + y: -130, 134 + stroke: 30, 135 + color: '#000000', 136 + width: 150, 137 + curve: 40, 138 + upperLipStroke: 30, 139 + upperLipCx1: -70, 140 + upperLipCy1: 20, 141 + upperLipCx2: 70, 142 + upperLipCy2: 30, 143 + lowerLipStroke: 30, 144 + lowerLipCx1: 40, 145 + lowerLipCy1: -10, 146 + lowerLipCx2: -40, 147 + lowerLipCy2: 5, 148 + mouthWidth: 200 149 + }, 150 + leftEye: { x: 75, y: 0, sizeX: 60, sizeY: 55, pupilSizeX: 25, pupilSizeY: 25 }, 151 + rightEye: { x: -75, y: 0, sizeX: 60, sizeY: 55, pupilSizeX: 25, pupilSizeY: 25 } 101 152 }, 102 153 surprised: { 103 154 leftEyebrow: { ··· 105 156 y: 120, 106 157 width: 100, 107 158 curve: 100, 108 - stroke: 15, 159 + stroke: 10, 109 160 angle: -10, 110 161 sharpness: 0, 111 162 color: '#515151' ··· 115 166 y: 120, 116 167 width: 100, 117 168 curve: 100, 118 - stroke: 15, 169 + stroke: 10, 119 170 angle: 10, 120 171 sharpness: 0, 121 172 color: '#515151' 122 173 }, 123 - mouth: { x: 0, y: -160, stroke: 100, color: '#000000', width: 40, curve: -5 }, 174 + mouth: { 175 + x: 0, 176 + y: -160, 177 + stroke: 100, 178 + color: '#000000', 179 + width: 70, 180 + curve: -5, 181 + upperLipStroke: 30, 182 + upperLipCx1: -30, 183 + upperLipCy1: 30, 184 + upperLipCx2: 40, 185 + upperLipCy2: 30, 186 + lowerLipStroke: 30, 187 + lowerLipCx1: 30, 188 + lowerLipCy1: -40, 189 + lowerLipCx2: -30, 190 + lowerLipCy2: -40, 191 + mouthWidth: 200 192 + }, 124 193 leftEye: { x: 75, y: 0, sizeX: 70, sizeY: 70, pupilSizeX: 30, pupilSizeY: 30 }, 125 194 rightEye: { x: -75, y: 0, sizeX: 70, sizeY: 70, pupilSizeX: 30, pupilSizeY: 30 } 126 195 }, ··· 145 214 sharpness: 0, 146 215 color: '#515151' 147 216 }, 148 - mouth: { x: 0, y: -130, stroke: 20, color: '#000000', width: 70, curve: 0 }, 149 - leftEye: { x: 75, y: -20, sizeX: 50, sizeY: 40, pupilSizeX: 20, pupilSizeY: 20 }, 150 - rightEye: { x: -75, y: -20, sizeX: 50, sizeY: 40, pupilSizeX: 20, pupilSizeY: 20 } 217 + mouth: { 218 + x: 0, 219 + y: -130, 220 + stroke: 20, 221 + color: '#000000', 222 + width: 70, 223 + curve: 0, 224 + upperLipStroke: 30, 225 + upperLipCx1: -10, 226 + upperLipCy1: -20, 227 + upperLipCx2: 10, 228 + upperLipCy2: 10, 229 + lowerLipStroke: 30, 230 + lowerLipCx1: 10, 231 + lowerLipCy1: 10, 232 + lowerLipCx2: -10, 233 + lowerLipCy2: -20, 234 + mouthWidth: 200 235 + }, 236 + leftEye: { x: 75, y: -20, sizeX: 60, sizeY: 50, pupilSizeX: 25, pupilSizeY: 25 }, 237 + rightEye: { x: -75, y: -20, sizeX: 60, sizeY: 50, pupilSizeX: 25, pupilSizeY: 25 } 151 238 }, 152 239 sad: { 153 240 leftEyebrow: { 154 241 x: 75, 155 - y: 80, 242 + y: 90, 156 243 width: 100, 157 244 curve: -20, 158 245 stroke: 10, ··· 162 249 }, 163 250 rightEyebrow: { 164 251 x: -75, 165 - y: 80, 252 + y: 90, 166 253 width: 100, 167 254 curve: -20, 168 255 stroke: 10, ··· 170 257 sharpness: 0, 171 258 color: '#515151' 172 259 }, 173 - mouth: { x: 0, y: -130, stroke: 20, color: '#000000', width: 100, curve: 20 }, 174 - leftEye: { x: 75, y: 0, sizeX: 60, sizeY: 60, pupilSizeX: 25, pupilSizeY: 25 }, 175 - rightEye: { x: -75, y: 0, sizeX: 60, sizeY: 60, pupilSizeX: 25, pupilSizeY: 25 } 260 + mouth: { 261 + x: 0, 262 + y: -130, 263 + stroke: 20, 264 + color: '#000000', 265 + width: 60, 266 + curve: 20, 267 + upperLipStroke: 30, 268 + upperLipCx1: -30, 269 + upperLipCy1: 20, 270 + upperLipCx2: 30, 271 + upperLipCy2: 20, 272 + lowerLipStroke: 30, 273 + lowerLipCx1: 30, 274 + lowerLipCy1: 10, 275 + lowerLipCx2: -50, 276 + lowerLipCy2: 10, 277 + mouthWidth: 200 278 + }, 279 + leftEye: { x: 75, y: 0, sizeX: 70, sizeY: 70, pupilSizeX: 25, pupilSizeY: 25 }, 280 + rightEye: { x: -75, y: 0, sizeX: 70, sizeY: 70, pupilSizeX: 25, pupilSizeY: 25 } 176 281 }, 177 282 excited: { 178 283 leftEyebrow: { ··· 195 300 sharpness: 0, 196 301 color: '#515151' 197 302 }, 198 - mouth: { x: 0, y: -130, stroke: 40, color: '#000000', width: 150, curve: -80 }, 199 - leftEye: { x: 75, y: 0, sizeX: 50, sizeY: 60, pupilSizeX: 25, pupilSizeY: 25 }, 200 - rightEye: { x: -75, y: 0, sizeX: 50, sizeY: 60, pupilSizeX: 25, pupilSizeY: 25 } 303 + mouth: { 304 + x: 0, 305 + y: -130, 306 + stroke: 40, 307 + color: '#000000', 308 + width: 180, 309 + curve: -80, 310 + upperLipStroke: 30, 311 + upperLipCx1: -30, 312 + upperLipCy1: -10, 313 + upperLipCx2: 30, 314 + upperLipCy2: -10, 315 + lowerLipStroke: 30, 316 + lowerLipCx1: 30, 317 + lowerLipCy1: -70, 318 + lowerLipCx2: -30, 319 + lowerLipCy2: -50, 320 + mouthWidth: 200 321 + }, 322 + leftEye: { x: 75, y: 0, sizeX: 60, sizeY: 70, pupilSizeX: 25, pupilSizeY: 25 }, 323 + rightEye: { x: -75, y: 0, sizeX: 60, sizeY: 70, pupilSizeX: 25, pupilSizeY: 25 } 324 + }, 325 + test: { 326 + leftEyebrow: { 327 + x: 75, 328 + y: 100, 329 + width: 100, 330 + curve: 40, 331 + stroke: 5, 332 + angle: 0, 333 + sharpness: 0, 334 + color: '#515151' 335 + }, 336 + rightEyebrow: { 337 + x: -75, 338 + y: 100, 339 + width: 100, 340 + curve: 40, 341 + stroke: 5, 342 + angle: 0, 343 + sharpness: 0, 344 + color: '#515151' 345 + }, 346 + mouth: { 347 + x: 0, 348 + y: -130, 349 + stroke: 40, 350 + color: '#000000', 351 + width: 150, 352 + curve: -80, 353 + upperLipStroke: 50, 354 + upperLipCurve: -40, 355 + lowerLipStroke: 50, 356 + lowerLipCurve: -120, 357 + mouthWidth: 200 358 + }, 359 + leftEye: { x: 75, y: 0, sizeX: 50, sizeY: 60, pupilSizeX: 80, pupilSizeY: 80 }, 360 + rightEye: { x: -75, y: 0, sizeX: 50, sizeY: 60, pupilSizeX: 80, pupilSizeY: 80 } 201 361 } 202 362 }, 203 363 colors: {