|
| 1 | +/** |
| 2 | + * @license |
| 3 | + * Copyright 2025 Scratch Foundation |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +import { ConstantProvider as ClassicConstantProvider } from "../constants"; |
| 8 | + |
| 9 | +export enum PathCapType { |
| 10 | + CAP = "CAP", |
| 11 | + BOWLER = "BOWLER", |
| 12 | +} |
| 13 | + |
| 14 | +export enum PathEarState { |
| 15 | + DOWN = "DOWN", |
| 16 | + UP = "UP", |
| 17 | +} |
| 18 | + |
| 19 | +export interface CatPathState { |
| 20 | + capType: PathCapType; |
| 21 | + ear1State: PathEarState; |
| 22 | + ear2State: PathEarState; |
| 23 | +} |
| 24 | + |
| 25 | +export class ConstantProvider extends ClassicConstantProvider { |
| 26 | + START_HAT_HEIGHT = 31.5; |
| 27 | + START_HAT_WIDTH = 96; |
| 28 | + |
| 29 | + BOWLER_HAT_HEIGHT = 35; |
| 30 | + |
| 31 | + FACE_OPACITY = 0.6; |
| 32 | + |
| 33 | + EYE_1_X = 59.2; |
| 34 | + EYE_1_Y = -3.3; |
| 35 | + EYE_2_X = 29.1; |
| 36 | + EYE_2_Y = -3.3; |
| 37 | + OPEN_EYE_RADIUS = 3.4; |
| 38 | + CLOSED_EYE_1_PATH = |
| 39 | + "M25.2-1.1c0.1,0,0.2,0,0.2,0l8.3-2.1l-7-4.8" + |
| 40 | + "c-0.5-0.3-1.1-0.2-1.4,0.3s-0.2,1.1,0.3,1.4L29-4.1l-4,1" + |
| 41 | + "c-0.5,0.1-0.9,0.7-0.7,1.2C24.3-1.4,24.7-1.1,25.2-1.1z"; |
| 42 | + CLOSED_EYE_2_PATH = |
| 43 | + "M62.4-1.1c-0.1,0-0.2,0-0.2,0l-8.3-2.1l7-4.8" + |
| 44 | + "c0.5-0.3,1.1-0.2,1.4,0.3s0.2,1.1-0.3,1.4l-3.4,2.3l4,1" + |
| 45 | + "c0.5,0.1,0.9,0.7,0.7,1.2C63.2-1.4,62.8-1.1,62.4-1.1z"; |
| 46 | + |
| 47 | + MOUTH_PATH = |
| 48 | + "M45.6,0.1c-0.9,0-1.7-0.3-2.3-0.9" + |
| 49 | + "c-0.6,0.6-1.3,0.9-2.2,0.9c-0.9,0-1.8-0.3-2.3-0.9c-1-1.1-1.1-2.6-1.1-2.8" + |
| 50 | + "c0-0.5,0.5-1,1-1l0,0c0.6,0,1,0.5,1,1c0,0.4,0.1,1.7,1.4,1.7" + |
| 51 | + "c0.5,0,0.7-0.2,0.8-0.3c0.3-0.3,0.4-1,0.4-1.3c0-0.1,0-0.1,0-0.2" + |
| 52 | + "c0-0.5,0.5-1,1-1l0,0c0.5,0,1,0.4,1,1c0,0,0,0.1,0,0.2" + |
| 53 | + "c0,0.3,0.1,0.9,0.4,1.2C44.8-2.2,45-2,45.5-2s0.7-0.2,0.8-0.3" + |
| 54 | + "c0.3-0.4,0.4-1.1,0.3-1.3c0-0.5,0.4-1,0.9-1.1c0.5,0,1,0.4,1.1,0.9" + |
| 55 | + "c0,0.2,0.1,1.8-0.8,2.8C47.5-0.4,46.8,0.1,45.6,0.1z"; |
| 56 | + |
| 57 | + EAR_INSIDE_COLOR = "#FFD5E6"; |
| 58 | + EAR_1_INSIDE_PATH = |
| 59 | + "M22.4-15.6c-1.7-4.2-4.5-9.1-5.8-8.5" + |
| 60 | + "c-1.6,0.8-5.4,7.9-5,15.4c0,0.6,0.7,0.7,1.1,0.5c3-1.6,6.4-2.8,8.6-3.6" + |
| 61 | + "C22.8-12.3,23.2-13.7,22.4-15.6z"; |
| 62 | + EAR_2_INSIDE_PATH = |
| 63 | + "M73.1-15.6c1.7-4.2,4.5-9.1,5.8-8.5" + |
| 64 | + "c1.6,0.8,5.4,7.9,5,15.4c0,0.6-0.7,0.7-1.1,0.5c-3-1.6-6.4-2.8-8.6-3.6" + |
| 65 | + "C72.8-12.3,72.4-13.7,73.1-15.6z"; |
| 66 | + |
| 67 | + CAP_START_PATH = "c2.6,-2.3 5.5,-4.3 8.5,-6.2"; |
| 68 | + CAP_MIDDLE_PATH = "c8.4,-1.3 17,-1.3 25.4,0"; |
| 69 | + CAP_END_PATH = "c3,1.8 5.9,3.9 8.5,6.1"; |
| 70 | + |
| 71 | + CAP_EAR_1_UP_PATH = |
| 72 | + "c-1,-12.5 5.3,-23.3 8.4,-24.8" + "c3.7,-1.8 16.5,13.1 18.4,15.4"; |
| 73 | + CAP_EAR_2_UP_PATH = |
| 74 | + "c1.9,-2.3 14.7,-17.2 18.4,-15.4" + "c3.1,1.5 9.4,12.3 8.4,24.8"; |
| 75 | + CAP_EAR_1_DOWN_PATH = |
| 76 | + "c-5.8,-4.8 -8,-18 -4.9,-19.5" + "c3.7,-1.8 24.5,11.1 31.7,10.1"; |
| 77 | + CAP_EAR_2_DOWN_PATH = |
| 78 | + "c7.2,1 28,-11.9 31.7,-10.1" + "c3.1,1.5 0.9,14.7 -4.9,19.5"; |
| 79 | + |
| 80 | + BOWLER_START_PATH = ""; // opening curve depends on whether ear 1 is up or down |
| 81 | + BOWLER_MIDDLE_PATH = "h33"; |
| 82 | + BOWLER_END_PATH = "a 20,20 0 0,1 20,20"; |
| 83 | + BOWLER_EAR_1_UP_PATH = |
| 84 | + "c0,-7.1 3.7,-13.3 9.3,-16.9" + |
| 85 | + "c1.7,-7.5 5.4,-13.2 7.6,-14.2" + |
| 86 | + "c2.6,-1.3 10,6 14.6,11.1"; |
| 87 | + BOWLER_EAR_2_UP_PATH = |
| 88 | + "c4.6,-5.1 11.9,-12.4 14.6,-11.1" + |
| 89 | + "c1.9,0.9 4.9,5.2 6.8,11.1" + |
| 90 | + "h7.8"; |
| 91 | + BOWLER_EAR_1_DOWN_PATH = |
| 92 | + "c0,-4.6 1.6,-8.9 4.3,-12.3" + |
| 93 | + "c-2.4,-5.6 -2.9,-12.4 -0.7,-13.4" + |
| 94 | + "c2.1,-1 9.6,2.6 17,5.8" + |
| 95 | + "h10.9"; |
| 96 | + BOWLER_EAR_2_DOWN_PATH = |
| 97 | + "h11" + |
| 98 | + "c7.4,-3.2 14.8,-6.8 16.9,-5.8" + |
| 99 | + "c1.2,0.6 1.6,2.9 1.3,5.8"; |
| 100 | + |
| 101 | + // This number was determined experimentally: |
| 102 | + // - The 17 came from zooming in on a "define" block and iterating to get a near-vertical edge. |
| 103 | + // - The .7 came from measuring the width of the other parts of the SVG path. |
| 104 | + BOWLER_WIDTH_MAGIC = 17.7; |
| 105 | + |
| 106 | + /** |
| 107 | + * Make the starting portion of a block's hat. |
| 108 | + * The return value will be stored as START_HAT. |
| 109 | + * In the case of cat blocks, this is just a placeholder for sizing. |
| 110 | + */ |
| 111 | + makeStartHat() { |
| 112 | + return { |
| 113 | + height: this.START_HAT_HEIGHT, |
| 114 | + width: this.START_HAT_WIDTH, |
| 115 | + path: this.makeCatPath(0, { |
| 116 | + capType: PathCapType.CAP, |
| 117 | + ear1State: PathEarState.UP, |
| 118 | + ear2State: PathEarState.UP, |
| 119 | + }), |
| 120 | + }; |
| 121 | + } |
| 122 | + |
| 123 | + makeCatPath(width: number, state: CatPathState) { |
| 124 | + const pathStart = this[`${state.capType}_START_PATH`]; |
| 125 | + const pathEar1 = |
| 126 | + this[`${state.capType}_EAR_1_${state.ear1State}_PATH`]; |
| 127 | + const pathMiddle = this[`${state.capType}_MIDDLE_PATH`]; |
| 128 | + const pathEar2 = |
| 129 | + this[`${state.capType}_EAR_2_${state.ear2State}_PATH`]; |
| 130 | + const spacer = (state.capType === PathCapType.BOWLER) |
| 131 | + ? `l ${width - this.START_HAT_WIDTH - this.BOWLER_WIDTH_MAGIC} 0` |
| 132 | + : ""; // allow cap logic to finish the path |
| 133 | + const pathEnd = this[`${state.capType}_END_PATH`]; |
| 134 | + return `${pathStart}${pathEar1}${pathMiddle}${pathEar2}${spacer}${pathEnd}`; |
| 135 | + } |
| 136 | +} |
0 commit comments