@mumulinya167/ccm-pet-assets 2.0.0 → 2.0.2

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.
Files changed (141) hide show
  1. package/assets/calico/calico-idle-follow.svg +324 -324
  2. package/assets/calico.svg +324 -324
  3. package/assets/clawd/LICENSE +22 -22
  4. package/assets/clawd/clawd-about-hero.svg +202 -202
  5. package/assets/clawd/clawd-collapse-sleep.svg +247 -247
  6. package/assets/clawd/clawd-error.svg +94 -94
  7. package/assets/clawd/clawd-happy.svg +160 -160
  8. package/assets/clawd/clawd-headphones-groove.svg +194 -194
  9. package/assets/clawd/clawd-idle-bubble.svg +180 -180
  10. package/assets/clawd/clawd-idle-collapse.svg +101 -101
  11. package/assets/clawd/clawd-idle-doze.svg +72 -72
  12. package/assets/clawd/clawd-idle-follow.svg +64 -64
  13. package/assets/clawd/clawd-idle-living.svg +195 -195
  14. package/assets/clawd/clawd-idle-look.svg +115 -115
  15. package/assets/clawd/clawd-idle-reading.svg +252 -252
  16. package/assets/clawd/clawd-idle-yawn.svg +158 -158
  17. package/assets/clawd/clawd-mini-alert.svg +129 -129
  18. package/assets/clawd/clawd-mini-crabwalk.svg +76 -76
  19. package/assets/clawd/clawd-mini-enter-sleep.svg +64 -64
  20. package/assets/clawd/clawd-mini-enter.svg +114 -114
  21. package/assets/clawd/clawd-mini-happy.svg +124 -124
  22. package/assets/clawd/clawd-mini-idle.svg +83 -83
  23. package/assets/clawd/clawd-mini-peek.svg +82 -82
  24. package/assets/clawd/clawd-mini-sleep.svg +112 -112
  25. package/assets/clawd/clawd-mini-typing.svg +153 -153
  26. package/assets/clawd/clawd-notification.svg +438 -438
  27. package/assets/clawd/clawd-react-annoyed.svg +167 -167
  28. package/assets/clawd/clawd-react-double-jump.svg +229 -229
  29. package/assets/clawd/clawd-react-double.svg +108 -108
  30. package/assets/clawd/clawd-react-drag.svg +102 -102
  31. package/assets/clawd/clawd-react-left.svg +102 -102
  32. package/assets/clawd/clawd-react-right.svg +102 -102
  33. package/assets/clawd/clawd-sleeping.svg +117 -117
  34. package/assets/clawd/clawd-static-base.svg +20 -20
  35. package/assets/clawd/clawd-wake.svg +277 -277
  36. package/assets/clawd/clawd-working-building.svg +329 -329
  37. package/assets/clawd/clawd-working-carrying.svg +178 -178
  38. package/assets/clawd/clawd-working-debugger.svg +245 -245
  39. package/assets/clawd/clawd-working-juggling.svg +183 -183
  40. package/assets/clawd/clawd-working-sweeping.svg +248 -248
  41. package/assets/clawd/clawd-working-thinking.svg +501 -501
  42. package/assets/clawd/clawd-working-typing-boss.svg +307 -307
  43. package/assets/clawd/clawd-working-typing.svg +273 -273
  44. package/assets/clawd/clawd-working-ultrathink.svg +166 -166
  45. package/assets/clawd/clawd-working-wizard.svg +356 -356
  46. package/assets/clawd.svg +64 -64
  47. package/assets/cloudling/cloudling/cloudling-attention.svg +379 -379
  48. package/assets/cloudling/cloudling/cloudling-building.svg +667 -667
  49. package/assets/cloudling/cloudling/cloudling-carrying.svg +604 -604
  50. package/assets/cloudling/cloudling/cloudling-conducting.svg +616 -616
  51. package/assets/cloudling/cloudling/cloudling-dozing-to-sleeping.svg +578 -578
  52. package/assets/cloudling/cloudling/cloudling-dozing.svg +358 -358
  53. package/assets/cloudling/cloudling/cloudling-error.svg +428 -428
  54. package/assets/cloudling/cloudling/cloudling-idle-reading.svg +658 -658
  55. package/assets/cloudling/cloudling/cloudling-idle-to-dozing.svg +568 -568
  56. package/assets/cloudling/cloudling/cloudling-idle-to-sleeping.svg +811 -811
  57. package/assets/cloudling/cloudling/cloudling-idle.svg +255 -255
  58. package/assets/cloudling/cloudling/cloudling-juggling.svg +648 -648
  59. package/assets/cloudling/cloudling/cloudling-mini-alert.svg +389 -389
  60. package/assets/cloudling/cloudling/cloudling-mini-crabwalk.svg +335 -335
  61. package/assets/cloudling/cloudling/cloudling-mini-enter-roll-in.svg +225 -225
  62. package/assets/cloudling/cloudling/cloudling-mini-enter-sleep.svg +231 -231
  63. package/assets/cloudling/cloudling/cloudling-mini-happy.svg +370 -370
  64. package/assets/cloudling/cloudling/cloudling-mini-idle.svg +279 -279
  65. package/assets/cloudling/cloudling/cloudling-mini-peek.svg +327 -327
  66. package/assets/cloudling/cloudling/cloudling-mini-sleep.svg +260 -260
  67. package/assets/cloudling/cloudling/cloudling-mini-typing.svg +691 -691
  68. package/assets/cloudling/cloudling/cloudling-notification.svg +493 -493
  69. package/assets/cloudling/cloudling/cloudling-react-drag.svg +83 -83
  70. package/assets/cloudling/cloudling/cloudling-sleeping-to-idle.svg +555 -555
  71. package/assets/cloudling/cloudling/cloudling-sleeping.svg +500 -500
  72. package/assets/cloudling/cloudling/cloudling-sweeping.svg +292 -292
  73. package/assets/cloudling/cloudling/cloudling-thinking.svg +719 -719
  74. package/assets/cloudling/cloudling/cloudling-typing.svg +354 -354
  75. package/assets/cloudling/cloudling-attention.svg +379 -379
  76. package/assets/cloudling/cloudling-building.svg +667 -667
  77. package/assets/cloudling/cloudling-carrying.svg +604 -604
  78. package/assets/cloudling/cloudling-conducting.svg +616 -616
  79. package/assets/cloudling/cloudling-dozing-to-sleeping.svg +578 -578
  80. package/assets/cloudling/cloudling-dozing.svg +358 -358
  81. package/assets/cloudling/cloudling-error.svg +428 -428
  82. package/assets/cloudling/cloudling-idle-reading.svg +658 -658
  83. package/assets/cloudling/cloudling-idle-to-dozing.svg +568 -568
  84. package/assets/cloudling/cloudling-idle-to-sleeping.svg +811 -811
  85. package/assets/cloudling/cloudling-idle.svg +255 -255
  86. package/assets/cloudling/cloudling-juggling.svg +648 -648
  87. package/assets/cloudling/cloudling-mini-alert.svg +389 -389
  88. package/assets/cloudling/cloudling-mini-crabwalk.svg +335 -335
  89. package/assets/cloudling/cloudling-mini-enter-roll-in.svg +225 -225
  90. package/assets/cloudling/cloudling-mini-enter-sleep.svg +231 -231
  91. package/assets/cloudling/cloudling-mini-happy.svg +370 -370
  92. package/assets/cloudling/cloudling-mini-idle.svg +279 -279
  93. package/assets/cloudling/cloudling-mini-peek.svg +327 -327
  94. package/assets/cloudling/cloudling-mini-sleep.svg +260 -260
  95. package/assets/cloudling/cloudling-mini-typing.svg +691 -691
  96. package/assets/cloudling/cloudling-notification.svg +493 -493
  97. package/assets/cloudling/cloudling-react-drag.svg +83 -83
  98. package/assets/cloudling/cloudling-sleeping-to-idle.svg +555 -555
  99. package/assets/cloudling/cloudling-sleeping.svg +500 -500
  100. package/assets/cloudling/cloudling-sweeping.svg +292 -292
  101. package/assets/cloudling/cloudling-thinking.svg +719 -719
  102. package/assets/cloudling/cloudling-typing.svg +354 -354
  103. package/assets/cloudling.svg +255 -255
  104. package/assets/ghost-attention.svg +19 -19
  105. package/assets/ghost-carrying.svg +19 -19
  106. package/assets/ghost-collapsing.svg +19 -19
  107. package/assets/ghost-dozing.svg +23 -23
  108. package/assets/ghost-error.svg +19 -19
  109. package/assets/ghost-happy.svg +23 -23
  110. package/assets/ghost-idle.svg +11 -11
  111. package/assets/ghost-juggling.svg +19 -19
  112. package/assets/ghost-notification.svg +19 -19
  113. package/assets/ghost-react-double.svg +23 -23
  114. package/assets/ghost-react-drag.svg +23 -23
  115. package/assets/ghost-react-left.svg +19 -19
  116. package/assets/ghost-react-right.svg +19 -19
  117. package/assets/ghost-sleeping.svg +19 -19
  118. package/assets/ghost-sweeping.svg +19 -19
  119. package/assets/ghost-waking.svg +19 -19
  120. package/assets/ghost-yawning.svg +23 -23
  121. package/assets/ghost.svg +11 -11
  122. package/assets/robot-attention.svg +19 -19
  123. package/assets/robot-carrying.svg +19 -19
  124. package/assets/robot-collapsing.svg +19 -19
  125. package/assets/robot-dozing.svg +23 -23
  126. package/assets/robot-error.svg +19 -19
  127. package/assets/robot-happy.svg +23 -23
  128. package/assets/robot-idle.svg +11 -11
  129. package/assets/robot-juggling.svg +19 -19
  130. package/assets/robot-notification.svg +19 -19
  131. package/assets/robot-react-double.svg +23 -23
  132. package/assets/robot-react-drag.svg +23 -23
  133. package/assets/robot-react-left.svg +19 -19
  134. package/assets/robot-react-right.svg +19 -19
  135. package/assets/robot-sleeping.svg +19 -19
  136. package/assets/robot-sweeping.svg +19 -19
  137. package/assets/robot-waking.svg +19 -19
  138. package/assets/robot-yawning.svg +23 -23
  139. package/assets/robot.svg +11 -11
  140. package/manifest.json +280 -280
  141. package/package.json +1 -1
@@ -1,616 +1,616 @@
1
- <svg id="stageSvg" xmlns="http://www.w3.org/2000/svg" width="880" height="720" viewBox="-32 -24 88 72" preserveAspectRatio="xMidYMid meet">
2
- <style><![CDATA[
3
- #stageBg { fill: #0f0f14; }
4
- .edge-rim-tight { stroke-opacity: .65; }
5
- .edge-rim-soft { stroke-opacity: .50; }
6
- .edge-contact { stroke-opacity: .65; }
7
- .orbit-track { stroke: rgba(177,167,255,.13); stroke-width: 0.13; fill: none; stroke-dasharray: 0.45 0.65; }
8
- .trail-spark { pointer-events: none; }
9
- .spark-shell { stroke: rgba(255,255,255,.82); stroke-width: 0.075; stroke-linejoin: round; }
10
- .spark-core { fill: #FFFDF0; opacity: .20; }
11
- @media (prefers-color-scheme: light) {
12
- #stageBg { fill: #fff; }
13
- .edge-rim-tight { stroke-opacity: .75; }
14
- .edge-rim-soft { stroke-opacity: .60; }
15
- .edge-contact { stroke-opacity: .75; }
16
- .cloud-drop-shadow { flood-opacity: .13; }
17
- .orbit-track { stroke: rgba(57,65,255,.22); }
18
- .spark-shell { stroke: rgba(255,255,255,.82); stroke-width: 0.075; }
19
- .spark-core { opacity: .20; }
20
- #taskTrailLayer use, #taskABack, #taskBBack, #taskCBack, #taskDBack, #taskAFront, #taskBFront, #taskCFront, #taskDFront { filter: url(#task-glow); }
21
- .food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
22
- .food-cloud > path {
23
- stroke: #5870E8 !important;
24
- stroke-width: 1.2 !important;
25
- stroke-opacity: .9 !important;
26
- vector-effect: non-scaling-stroke !important;
27
- }
28
- }
29
- ]]></style>
30
- <defs>
31
- <linearGradient id="body-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
32
- <stop offset="0" stop-color="#f0f9ff"/>
33
- <stop offset="0.5" stop-color="#e0f2fe"/>
34
- <stop offset="1" stop-color="#bae6fd"/>
35
- </linearGradient>
36
- <linearGradient id="eye-grad" x1="12" y1="3" x2="12" y2="21" gradientUnits="userSpaceOnUse">
37
- <stop offset="0" stop-color="#B1A7FF"/>
38
- <stop offset="0.5" stop-color="#7A9DFF"/>
39
- <stop offset="1" stop-color="#3941FF"/>
40
- </linearGradient>
41
- <radialGradient id="hi-grad" cx="0.45" cy="0.25" r="0.55">
42
- <stop offset="0" stop-color="#f0f9ff" stop-opacity="0.9"/>
43
- <stop offset="1" stop-color="#f0f9ff" stop-opacity="0"/>
44
- </radialGradient>
45
- <radialGradient id="taskA-grad" cx="0.48" cy="0.38" r="0.82">
46
- <stop offset="0" stop-color="#FFF7D1"/>
47
- <stop offset="0.58" stop-color="#FFD978"/>
48
- <stop offset="1" stop-color="#DFA93C"/>
49
- </radialGradient>
50
- <radialGradient id="taskB-grad" cx="0.48" cy="0.38" r="0.82">
51
- <stop offset="0" stop-color="#F7F0FF"/>
52
- <stop offset="0.58" stop-color="#CDBBFF"/>
53
- <stop offset="1" stop-color="#8F78D7"/>
54
- </radialGradient>
55
- <radialGradient id="taskC-grad" cx="0.48" cy="0.38" r="0.82">
56
- <stop offset="0" stop-color="#F1FFFC"/>
57
- <stop offset="0.58" stop-color="#9FE5DD"/>
58
- <stop offset="1" stop-color="#50AFA6"/>
59
- </radialGradient>
60
- <radialGradient id="taskD-grad" cx="0.48" cy="0.38" r="0.82">
61
- <stop offset="0" stop-color="#FFF3FA"/>
62
- <stop offset="0.58" stop-color="#F7AFD4"/>
63
- <stop offset="1" stop-color="#CE6FA6"/>
64
- </radialGradient>
65
- <filter id="cloud-drop" x="-45%" y="-45%" width="190%" height="190%">
66
- <feDropShadow class="cloud-drop-shadow" dx="0" dy="0.6" stdDeviation="0.8" flood-color="#3941FF" flood-opacity="0.18"/>
67
- </filter>
68
- <filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
69
- <feGaussianBlur stdDeviation="0.16"/>
70
- </filter>
71
- <filter id="task-glow" x="-85%" y="-85%" width="270%" height="270%">
72
- <feGaussianBlur stdDeviation="0.16" result="b"/>
73
- <feMerge>
74
- <feMergeNode in="b"/>
75
- <feMergeNode in="SourceGraphic"/>
76
- </feMerge>
77
- </filter>
78
- <filter id="task-light-glow" x="-90%" y="-90%" width="280%" height="280%">
79
- <feDropShadow dx="0" dy="0.06" stdDeviation="0.18" flood-color="#4A4E76" flood-opacity="0.30"/>
80
- <feGaussianBlur stdDeviation="0.08" result="b"/>
81
- <feMerge>
82
- <feMergeNode in="b"/>
83
- <feMergeNode in="SourceGraphic"/>
84
- </feMerge>
85
- </filter>
86
- <clipPath id="cloud-clip">
87
- <path id="cloud-clip-path"/>
88
- </clipPath>
89
- <symbol id="taskA-symbol" viewBox="-1.22 -1.22 2.44 2.44">
90
- <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
91
- fill="url(#taskA-grad)" opacity="0.98"/>
92
- <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
93
- class="spark-core"/>
94
- </symbol>
95
- <symbol id="taskB-symbol" viewBox="-1.22 -1.22 2.44 2.44">
96
- <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
97
- fill="url(#taskB-grad)" opacity="0.98"/>
98
- <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
99
- class="spark-core"/>
100
- </symbol>
101
- <symbol id="taskC-symbol" viewBox="-1.22 -1.22 2.44 2.44">
102
- <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
103
- fill="url(#taskC-grad)" opacity="0.98"/>
104
- <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
105
- class="spark-core"/>
106
- </symbol>
107
- <symbol id="taskD-symbol" viewBox="-1.22 -1.22 2.44 2.44">
108
- <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
109
- fill="url(#taskD-grad)" opacity="0.98"/>
110
- <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
111
- class="spark-core"/>
112
- </symbol>
113
- </defs>
114
- <g id="orbitTrackLayer">
115
- <path id="orbitTrackA" class="orbit-track"/>
116
- <path id="orbitTrackB" class="orbit-track"/>
117
- </g>
118
-
119
- <g id="taskTrailLayer">
120
- <g id="trailA" class="trail-spark">
121
- <use id="trailA0" href="#taskA-symbol" filter="url(#task-glow)"/>
122
- <use id="trailA1" href="#taskA-symbol" filter="url(#task-glow)"/>
123
- <use id="trailA2" href="#taskA-symbol" filter="url(#task-glow)"/>
124
- <use id="trailA3" href="#taskA-symbol" filter="url(#task-glow)"/>
125
- </g>
126
- <g id="trailB" class="trail-spark">
127
- <use id="trailB0" href="#taskB-symbol" filter="url(#task-glow)"/>
128
- <use id="trailB1" href="#taskB-symbol" filter="url(#task-glow)"/>
129
- <use id="trailB2" href="#taskB-symbol" filter="url(#task-glow)"/>
130
- <use id="trailB3" href="#taskB-symbol" filter="url(#task-glow)"/>
131
- </g>
132
- <g id="trailC" class="trail-spark">
133
- <use id="trailC0" href="#taskC-symbol" filter="url(#task-glow)"/>
134
- <use id="trailC1" href="#taskC-symbol" filter="url(#task-glow)"/>
135
- <use id="trailC2" href="#taskC-symbol" filter="url(#task-glow)"/>
136
- <use id="trailC3" href="#taskC-symbol" filter="url(#task-glow)"/>
137
- </g>
138
- <g id="trailD" class="trail-spark">
139
- <use id="trailD0" href="#taskD-symbol" filter="url(#task-glow)"/>
140
- <use id="trailD1" href="#taskD-symbol" filter="url(#task-glow)"/>
141
- <use id="trailD2" href="#taskD-symbol" filter="url(#task-glow)"/>
142
- <use id="trailD3" href="#taskD-symbol" filter="url(#task-glow)"/>
143
- </g>
144
- </g>
145
-
146
- <use id="taskABack" href="#taskA-symbol" filter="url(#task-glow)"/>
147
- <use id="taskBBack" href="#taskB-symbol" filter="url(#task-glow)"/>
148
- <use id="taskCBack" href="#taskC-symbol" filter="url(#task-glow)"/>
149
- <use id="taskDBack" href="#taskD-symbol" filter="url(#task-glow)"/>
150
-
151
- <g id="cloudRoot">
152
- <g id="cloudEdge">
153
- <path id="edgeContact" class="edge-contact" fill="none" stroke="#7d8aa7" stroke-width="1.05" stroke-linejoin="round" transform="translate(0 0.18)" filter="url(#edge-blur)"/>
154
- <path id="edgeRimSoft" class="edge-rim-soft" fill="none" stroke="#7A9DFF" stroke-width="0.90" stroke-linejoin="round" filter="url(#edge-blur)"/>
155
- <path id="edgeRimTight" class="edge-rim-tight" fill="none" stroke="#aab3c6" stroke-width="0.42" stroke-linejoin="round"/>
156
- </g>
157
- <g id="cloudGroup" filter="url(#cloud-drop)">
158
- <path id="cloudPath" fill="url(#body-grad)"/>
159
- <rect x="1" y="2" width="22" height="11" fill="url(#hi-grad)" clip-path="url(#cloud-clip)"/>
160
- </g>
161
- <g id="eyeGroup">
162
- <rect id="eyeL" x="7.7" y="9.5" width="2.6" height="5" rx="1.3" ry="1.3" fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7"/>
163
- <rect id="eyeR" x="13.7" y="9.5" width="2.6" height="5" rx="1.3" ry="1.3" fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7"/>
164
- </g>
165
- </g>
166
-
167
- <use id="taskAFront" href="#taskA-symbol" filter="url(#task-glow)"/>
168
- <use id="taskBFront" href="#taskB-symbol" filter="url(#task-glow)"/>
169
- <use id="taskCFront" href="#taskC-symbol" filter="url(#task-glow)"/>
170
- <use id="taskDFront" href="#taskD-symbol" filter="url(#task-glow)"/>
171
- <g id="__cloudling-export-shims" opacity="0" pointer-events="none" style="display:none">
172
- <g id="readout"></g>
173
- <g id="scrub"></g>
174
- <g id="scrubV"></g>
175
- </g>
176
-
177
- <script type="application/ecmascript"><![CDATA[
178
- const CLOUD_D = "M9.064 3.344a4.578 4.578 0 012.285-.312c1 .115 1.891.54 2.673 1.275.01.01.024.017.037.021a.09.09 0 00.043 0 4.55 4.55 0 013.046.275l.047.022.116.057a4.581 4.581 0 012.188 2.399c.209.51.313 1.041.315 1.595a4.24 4.24 0 01-.134 1.223.123.123 0 00.03.115c.594.607.988 1.33 1.183 2.17.289 1.425-.007 2.71-.887 3.854l-.136.166a4.548 4.548 0 01-2.201 1.388.123.123 0 00-.081.076c-.191.551-.383 1.023-.74 1.494-.9 1.187-2.222 1.846-3.711 1.838-1.187-.006-2.239-.44-3.157-1.302a.107.107 0 00-.105-.024c-.388.125-.78.143-1.204.138a4.441 4.441 0 01-1.945-.466 4.544 4.544 0 01-1.61-1.335c-.152-.202-.303-.392-.414-.617a5.81 5.81 0 01-.37-.961 4.582 4.582 0 01-.014-2.298.124.124 0 00.006-.056.085.085 0 00-.027-.048 4.467 4.467 0 01-1.034-1.651 3.896 3.896 0 01-.251-1.192 5.189 5.189 0 01.141-1.6c.337-1.112.982-1.985 1.933-2.618.212-.141.413-.251.601-.33.215-.089.43-.164.646-.227a.098.098 0 00.065-.066 4.51 4.51 0 01.829-1.615 4.535 4.535 0 011.837-1.388z";
179
-
180
- const CENTER = { x: 12, y: 12 };
181
-
182
- const els = {
183
- cloudRoot: document.getElementById('cloudRoot'),
184
- cloudPath: document.getElementById('cloudPath'),
185
- cloudClipPath: document.getElementById('cloud-clip-path'),
186
- edgeContact: document.getElementById('edgeContact'),
187
- edgeRimSoft: document.getElementById('edgeRimSoft'),
188
- edgeRimTight: document.getElementById('edgeRimTight'),
189
- eyeGroup: document.getElementById('eyeGroup'),
190
- eyeL: document.getElementById('eyeL'),
191
- eyeR: document.getElementById('eyeR'),
192
- eyeGrad: document.getElementById('eye-grad'),
193
- taskAFront: document.getElementById('taskAFront'),
194
- taskABack: document.getElementById('taskABack'),
195
- taskBFront: document.getElementById('taskBFront'),
196
- taskBBack: document.getElementById('taskBBack'),
197
- taskCFront: document.getElementById('taskCFront'),
198
- taskCBack: document.getElementById('taskCBack'),
199
- taskDFront: document.getElementById('taskDFront'),
200
- taskDBack: document.getElementById('taskDBack'),
201
- trailA: document.getElementById('trailA'),
202
- trailA0: document.getElementById('trailA0'),
203
- trailA1: document.getElementById('trailA1'),
204
- trailA2: document.getElementById('trailA2'),
205
- trailA3: document.getElementById('trailA3'),
206
- trailB: document.getElementById('trailB'),
207
- trailB0: document.getElementById('trailB0'),
208
- trailB1: document.getElementById('trailB1'),
209
- trailB2: document.getElementById('trailB2'),
210
- trailB3: document.getElementById('trailB3'),
211
- trailC: document.getElementById('trailC'),
212
- trailC0: document.getElementById('trailC0'),
213
- trailC1: document.getElementById('trailC1'),
214
- trailC2: document.getElementById('trailC2'),
215
- trailC3: document.getElementById('trailC3'),
216
- trailD: document.getElementById('trailD'),
217
- trailD0: document.getElementById('trailD0'),
218
- trailD1: document.getElementById('trailD1'),
219
- trailD2: document.getElementById('trailD2'),
220
- trailD3: document.getElementById('trailD3'),
221
- orbitTrackA: document.getElementById('orbitTrackA'),
222
- orbitTrackB: document.getElementById('orbitTrackB'),
223
- orbitTrackLayer: document.getElementById('orbitTrackLayer'),
224
- readout: document.getElementById('readout'),
225
- scrub: document.getElementById('scrub'),
226
- scrubV: document.getElementById('scrubV')
227
- };
228
-
229
- const P = {
230
- durA: 4.0, rxA: 14, ryA: 4, tiltA: -10, theta0A: 0,
231
- durB: 6.0, rxB: 4, ryB: 11, tiltB: 20, theta0B: 180,
232
- starSize: 1.75,
233
- backScale: 0.74,
234
- backOpacity: 0.28,
235
- depthSnap: 0.16,
236
- starSpin: 65,
237
- slotRadius: 14.0,
238
- slotSquash: 0.96,
239
- slotRot: 0,
240
- slotAX: 0, slotAY: 0,
241
- slotBX: 0, slotBY: 0,
242
- slotCX: 0, slotCY: 0,
243
- slotDX: 0, slotDY: 0,
244
- bodyBreath: 0.022,
245
- bodyFloat: 0.16,
246
- bodyLean: 0.48,
247
- bodyTilt: 1.15,
248
- callMorph: 0.075,
249
- syncMorph: 0.16,
250
- morphAmt: 0.04,
251
- morphPeriod: 5.0,
252
- cloudR: 7.5,
253
- eyeShift: 1.10,
254
- eyeSwitchT: 1.15,
255
- saccade: 0.13,
256
- eyeFlowPeriod: 5.5,
257
- eyeFlowAmp: 4,
258
- blinkInterval: 6.0,
259
- blinkDur: 0.16,
260
- showTracks: false,
261
- pause: false
262
- };
263
-
264
- const lerp = (a, b, t) => a + (b - a) * t;
265
- const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
266
- const smoothstep = t => t * t * (3 - 2 * t);
267
-
268
- const EYE_BASE = {
269
- l: { y: 9.5, h: 5, rx: 1.3 },
270
- r: { y: 9.5, h: 5, rx: 1.3 }
271
- };
272
-
273
- const TASK_ORDER = ['A', 'B', 'C', 'D'];
274
- const TASKS = [
275
- { key: 'A', orbit: 'A', phase: 8, slotAngle: 235, spin: 0.35, front: els.taskAFront, back: els.taskABack, trail: [els.trailA0, els.trailA1, els.trailA2, els.trailA3] },
276
- { key: 'B', orbit: 'B', phase: 72, slotAngle: 324, spin: -0.28, front: els.taskBFront, back: els.taskBBack, trail: [els.trailB0, els.trailB1, els.trailB2, els.trailB3] },
277
- { key: 'C', orbit: 'A', phase: 145, slotAngle: 53, spin: 0.30, front: els.taskCFront, back: els.taskCBack, trail: [els.trailC0, els.trailC1, els.trailC2, els.trailC3] },
278
- { key: 'D', orbit: 'B', phase: 252, slotAngle: 142, spin: -0.32, front: els.taskDFront, back: els.taskDBack, trail: [els.trailD0, els.trailD1, els.trailD2, els.trailD3] }
279
- ];
280
-
281
- function samplePath(d, N) {
282
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
283
- path.setAttribute('d', d);
284
- const pts = [];
285
- const len = path.getTotalLength();
286
- for (let i = 0; i < N; i++) {
287
- const p = path.getPointAtLength(i / N * len);
288
- pts.push({ x: p.x, y: p.y });
289
- }
290
- return pts;
291
- }
292
-
293
- const basePoints = samplePath(CLOUD_D, 64);
294
-
295
- function morphPoints(pts, R, t) {
296
- return pts.map(p => {
297
- const dx = p.x - CENTER.x;
298
- const dy = p.y - CENTER.y;
299
- const a = Math.atan2(dy, dx);
300
- return {
301
- x: lerp(p.x, CENTER.x + R * Math.cos(a), t),
302
- y: lerp(p.y, CENTER.y + R * Math.sin(a), t)
303
- };
304
- });
305
- }
306
-
307
- function pointsToSmoothPath(pts) {
308
- const n = pts.length;
309
- let d = `M ${pts[0].x.toFixed(3)} ${pts[0].y.toFixed(3)}`;
310
- for (let i = 0; i < n; i++) {
311
- const p0 = pts[(i - 1 + n) % n];
312
- const p1 = pts[i];
313
- const p2 = pts[(i + 1) % n];
314
- const p3 = pts[(i + 2) % n];
315
- const c1x = p1.x + (p2.x - p0.x) / 6;
316
- const c1y = p1.y + (p2.y - p0.y) / 6;
317
- const c2x = p2.x - (p3.x - p1.x) / 6;
318
- const c2y = p2.y - (p3.y - p1.y) / 6;
319
- d += ` C ${c1x.toFixed(3)} ${c1y.toFixed(3)} ${c2x.toFixed(3)} ${c2y.toFixed(3)} ${p2.x.toFixed(3)} ${p2.y.toFixed(3)}`;
320
- }
321
- return d + ' Z';
322
- }
323
-
324
- function orbitPoint(thetaDeg, rx, ry, tiltDeg) {
325
- const t = thetaDeg * Math.PI / 180;
326
- const r = tiltDeg * Math.PI / 180;
327
- const ct = Math.cos(t), st = Math.sin(t);
328
- const cr = Math.cos(r), sr = Math.sin(r);
329
- return {
330
- x: CENTER.x + rx * ct * cr - ry * st * sr,
331
- y: CENTER.y + rx * ct * sr + ry * st * cr
332
- };
333
- }
334
-
335
- function orbitTrackD(rx, ry, tiltDeg, samples) {
336
- let d = '';
337
- for (let i = 0; i <= samples; i++) {
338
- const theta = i / samples * 360;
339
- const p = orbitPoint(theta, rx, ry, tiltDeg);
340
- d += i === 0 ? `M ${p.x.toFixed(3)} ${p.y.toFixed(3)}` : ` L ${p.x.toFixed(3)} ${p.y.toFixed(3)}`;
341
- }
342
- return d + ' Z';
343
- }
344
-
345
- function getBeat(now) {
346
- const beatT = Math.max(P.eyeSwitchT, 0.1);
347
- const cycleT = beatT * 5;
348
- const cyclePos = (now % cycleT) / beatT;
349
- const beatIndex = Math.floor(cyclePos);
350
- const beatPhase = cyclePos - beatIndex;
351
- return {
352
- beatT,
353
- cyclePos,
354
- beatIndex,
355
- beatPhase,
356
- target: beatIndex < TASK_ORDER.length ? TASK_ORDER[beatIndex] : 'SYNC',
357
- sync: beatIndex >= TASK_ORDER.length
358
- };
359
- }
360
-
361
- function getOrbitParams(task) {
362
- return task.orbit === 'A'
363
- ? { dur: P.durA, theta0: P.theta0A, rx: P.rxA, ry: P.ryA, tilt: P.tiltA }
364
- : { dur: P.durB, theta0: P.theta0B, rx: P.rxB, ry: P.ryB, tilt: P.tiltB };
365
- }
366
-
367
- function getTaskSlot(task) {
368
- const a = (task.slotAngle + P.slotRot) * Math.PI / 180;
369
- return {
370
- x: CENTER.x + Math.cos(a) * P.slotRadius + P[`slot${task.key}X`],
371
- y: CENTER.y + Math.sin(a) * P.slotRadius * P.slotSquash + P[`slot${task.key}Y`]
372
- };
373
- }
374
-
375
- function slotMixForTask(task, beat) {
376
- const idx = TASK_ORDER.indexOf(task.key);
377
- const callLocal = beat.cyclePos - idx;
378
- if (callLocal < 0 || callLocal > 4.88) return 0;
379
- const arrive = smoothstep(clamp((callLocal - 0.04) / 0.22, 0, 1));
380
- const release = beat.cyclePos > 4.55 ? 1 - smoothstep(clamp((beat.cyclePos - 4.55) / 0.33, 0, 1)) : 1;
381
- return arrive * release;
382
- }
383
-
384
- function getTaskState(now, task, beat) {
385
- const p = getOrbitParams(task);
386
- const progress = (now / p.dur) % 1;
387
- const theta = p.theta0 + task.phase + progress * 360;
388
- const depth0 = Math.sin(theta * Math.PI / 180);
389
- const orbitPos = orbitPoint(theta, p.rx, p.ry, p.tilt);
390
- const slot = getTaskSlot(task);
391
- const slotMix = slotMixForTask(task, beat);
392
- const pos = {
393
- x: lerp(orbitPos.x, slot.x, slotMix),
394
- y: lerp(orbitPos.y, slot.y, slotMix)
395
- };
396
- const called = beat.target === task.key;
397
- const callPulse = called ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
398
- const syncPulse = beat.sync ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
399
- const frontLift = Math.max(slotMix, callPulse, syncPulse * 0.48);
400
- const depth = lerp(depth0, 0.68, frontLift);
401
- const scaleBoost = 1 + callPulse * 0.14 + syncPulse * 0.10 + slotMix * 0.04;
402
- const glowBoost = callPulse * 0.24 + syncPulse * 0.20 + slotMix * 0.10;
403
- return { ...task, p: pos, orbitPos, slot, depth, theta, progress, slotMix, called, callPulse, syncPulse, scaleBoost, glowBoost };
404
- }
405
-
406
- function setTaskLayer(state, baseSize, spinDeg) {
407
- const snap = P.depthSnap;
408
- const depthFrontOp = smoothstep(clamp((state.depth - snap) / (0.58 - snap), 0, 1));
409
- const frontOp = Math.max(depthFrontOp, state.callPulse * 0.72, state.syncPulse * 0.58);
410
- const baseBack = state.depth < snap ? P.backOpacity : P.backOpacity * (1 - frontOp);
411
- const frontBoost = 1 + state.glowBoost;
412
- const backBoost = 1 + state.glowBoost * 0.45;
413
- const sizeFront = baseSize * state.scaleBoost;
414
- const sizeBack = baseSize * P.backScale * (0.96 + state.scaleBoost * 0.04);
415
- const setUse = (el, sz, op) => {
416
- el.setAttribute('x', (-sz).toFixed(3));
417
- el.setAttribute('y', (-sz).toFixed(3));
418
- el.setAttribute('width', (2 * sz).toFixed(3));
419
- el.setAttribute('height', (2 * sz).toFixed(3));
420
- el.setAttribute('transform', `translate(${state.p.x.toFixed(3)} ${state.p.y.toFixed(3)}) rotate(${spinDeg.toFixed(2)})`);
421
- el.setAttribute('opacity', op.toFixed(3));
422
- };
423
- setUse(state.front, sizeFront, clamp(frontOp * frontBoost, 0, 1));
424
- setUse(state.back, sizeBack, clamp(baseBack * backBoost, 0, 0.8));
425
- }
426
-
427
- function taskTrailPoint(state, index) {
428
- const p = getOrbitParams(state);
429
- return orbitPoint(state.theta - 8 - index * 6.5, p.rx, p.ry, p.tilt);
430
- }
431
-
432
- function setTaskTrail(state) {
433
- const depthPresence = 0.52 + Math.max(state.depth, 0) * 0.35;
434
- const baseOp = (1 - state.slotMix) * depthPresence;
435
- const trailOps = [0.17, 0.09, 0.05, 0.025];
436
- const trailSizes = [0.28, 0.20, 0.14, 0.09];
437
- state.trail.forEach((el, i) => {
438
- const pos = taskTrailPoint(state, i);
439
- const sz = P.starSize * trailSizes[i];
440
- el.setAttribute('x', (-sz).toFixed(3));
441
- el.setAttribute('y', (-sz).toFixed(3));
442
- el.setAttribute('width', (2 * sz).toFixed(3));
443
- el.setAttribute('height', (2 * sz).toFixed(3));
444
- el.setAttribute('transform', `translate(${pos.x.toFixed(3)} ${pos.y.toFixed(3)}) rotate(${(state.theta * 0.28 + i * 17).toFixed(2)})`);
445
- el.setAttribute('opacity', clamp(baseOp * trailOps[i], 0, 0.3).toFixed(3));
446
- });
447
- }
448
-
449
- let nextBlinkT = 2.4;
450
- let blinkActive = false;
451
- let blinkStartT = 0;
452
- function getBlinkScale(t) {
453
- if (blinkActive && t < blinkStartT) {
454
- blinkActive = false;
455
- nextBlinkT = t + 2.4;
456
- return 1;
457
- }
458
- if (!blinkActive && t > nextBlinkT) {
459
- blinkActive = true;
460
- blinkStartT = t;
461
- }
462
- if (blinkActive) {
463
- const dt = t - blinkStartT;
464
- const half = P.blinkDur / 2;
465
- if (dt < half) return 1 - 0.94 * (dt / half);
466
- if (dt < P.blinkDur) return 0.06 + 0.94 * ((dt - half) / half);
467
- blinkActive = false;
468
- nextBlinkT = t + P.blinkInterval * (0.82 + Math.random() * 0.36);
469
- }
470
- return 1;
471
- }
472
-
473
- function applyBlink(rect, base, scale) {
474
- const h = Math.max(base.h * scale, 0.15);
475
- const y = base.y + (base.h - h) / 2;
476
- const rx = Math.min(base.rx, h / 2);
477
- rect.setAttribute('height', h.toFixed(3));
478
- rect.setAttribute('y', y.toFixed(3));
479
- rect.setAttribute('rx', rx.toFixed(3));
480
- rect.setAttribute('ry', rx.toFixed(3));
481
- }
482
-
483
- function getTargetPoint(key, states) {
484
- if (key === 'SYNC') return { x: CENTER.x, y: CENTER.y - 2.2 };
485
- return states.find(s => s.key === key)?.p || CENTER;
486
- }
487
-
488
- function getEyeTarget(beat, states) {
489
- const target = beat.target;
490
- const prevKey = beat.beatIndex === 0 ? 'SYNC' : TASK_ORDER[beat.beatIndex - 1];
491
- const oldPos = getTargetPoint(prevKey, states);
492
- const newPos = getTargetPoint(target, states);
493
- const blendT = clamp((beat.beatPhase * beat.beatT) / P.saccade, 0, 1);
494
- const eased = smoothstep(blendT);
495
- return {
496
- x: lerp(oldPos.x, newPos.x, eased),
497
- y: lerp(oldPos.y, newPos.y, eased),
498
- target,
499
- saccading: blendT < 1
500
- };
501
- }
502
-
503
- function getBodyResponse(now, beat, states) {
504
- const called = states.find(s => s.key === beat.target);
505
- const syncPulse = beat.sync ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
506
- const callPulse = called ? called.callPulse : 0;
507
- const focus = called ? called.p : { x: CENTER.x, y: CENTER.y - 2.4 };
508
- const dx = focus.x - CENTER.x;
509
- const dy = focus.y - CENTER.y;
510
- const len = Math.max(Math.hypot(dx, dy), 0.001);
511
- const nx = dx / len;
512
- const ny = dy / len;
513
- const breath = Math.sin(2 * Math.PI * now / 3.8);
514
- const softFloat = Math.sin(2 * Math.PI * now / 4.6 - 0.4);
515
- const idleTilt = Math.sin(2 * Math.PI * now / 5.2 + 0.7) * 0.42;
516
- const lean = callPulse * P.bodyLean;
517
- const tx = nx * lean;
518
- const ty = ny * lean * 0.48 + softFloat * P.bodyFloat;
519
- const rot = idleTilt + nx * callPulse * P.bodyTilt + syncPulse * 0.35 * Math.sin(2 * Math.PI * now / 0.9);
520
- const sx = 1 + breath * P.bodyBreath * 0.62 + callPulse * 0.012 + syncPulse * 0.026;
521
- const sy = 1 + breath * P.bodyBreath - callPulse * 0.006 + syncPulse * 0.020;
522
- const morphAdd = callPulse * P.callMorph + syncPulse * P.syncMorph;
523
- return { tx, ty, rot, sx, sy, callPulse, syncPulse, morphAdd };
524
- }
525
-
526
- function setCloudRootTransform(body) {
527
- els.cloudRoot.setAttribute(
528
- 'transform',
529
- `translate(${(CENTER.x + body.tx).toFixed(3)} ${(CENTER.y + body.ty).toFixed(3)}) rotate(${body.rot.toFixed(3)}) scale(${body.sx.toFixed(4)} ${body.sy.toFixed(4)}) translate(${-CENTER.x} ${-CENTER.y})`
530
- );
531
- }
532
-
533
- function renderAt(now) {
534
- const beat = getBeat(now);
535
- const taskStates = TASKS.map(task => getTaskState(now, task, beat));
536
- const body = getBodyResponse(now, beat, taskStates);
537
- setCloudRootTransform(body);
538
-
539
- const morphPhase = (now / P.morphPeriod) * 2 * Math.PI;
540
- const morph = clamp(((Math.sin(morphPhase) * 0.5 + 0.5) * P.morphAmt) + body.morphAdd, 0, 0.32);
541
- const cloudD = pointsToSmoothPath(morphPoints(basePoints, P.cloudR, morph));
542
- els.cloudPath.setAttribute('d', cloudD);
543
- els.cloudClipPath.setAttribute('d', cloudD);
544
- els.edgeContact.setAttribute('d', cloudD);
545
- els.edgeRimSoft.setAttribute('d', cloudD);
546
- els.edgeRimTight.setAttribute('d', cloudD);
547
-
548
- els.orbitTrackA.setAttribute('d', orbitTrackD(P.rxA, P.ryA, P.tiltA, 96));
549
- els.orbitTrackB.setAttribute('d', orbitTrackD(P.rxB, P.ryB, P.tiltB, 96));
550
- els.orbitTrackLayer.setAttribute('opacity', P.showTracks ? 1 : 0);
551
-
552
- const spin = (now * P.starSpin) % 360;
553
- taskStates.forEach(state => {
554
- setTaskTrail(state);
555
- setTaskLayer(state, P.starSize, spin * state.spin);
556
- });
557
-
558
- const target = getEyeTarget(beat, taskStates);
559
- const lookDx = target.x - CENTER.x;
560
- const lookDy = target.y - CENTER.y;
561
- const lookLen = Math.max(Math.hypot(lookDx, lookDy), 0.001);
562
- const eyeX = lookDx / lookLen * P.eyeShift;
563
- const eyeY = lookDy / lookLen * P.eyeShift * 0.55;
564
- els.eyeGroup.setAttribute('transform', `translate(${eyeX.toFixed(3)} ${eyeY.toFixed(3)})`);
565
-
566
- const blinkScale = getBlinkScale(now);
567
- applyBlink(els.eyeL, EYE_BASE.l, blinkScale);
568
- applyBlink(els.eyeR, EYE_BASE.r, blinkScale);
569
- const flowOffset = Math.sin(2 * Math.PI * now / P.eyeFlowPeriod) * P.eyeFlowAmp;
570
- els.eyeGrad.setAttribute('y1', (3 + flowOffset).toFixed(2));
571
- els.eyeGrad.setAttribute('y2', (21 + flowOffset).toFixed(2));
572
-
573
- const calledState = taskStates.find(s => s.key === target.target);
574
- const orderedCount = taskStates.filter(s => s.slotMix > 0.82).length;
575
- const closestPair = taskStates.reduce((best, a, i) => {
576
- for (let j = i + 1; j < taskStates.length; j++) {
577
- const b = taskStates[j];
578
- const dist = Math.hypot(a.p.x - b.p.x, a.p.y - b.p.y);
579
- if (dist < best.dist) best = { dist, pair: `${a.key}${b.key}` };
580
- }
581
- return best;
582
- }, { dist: Infinity, pair: '--' });
583
-
584
- els.scrub.value = Math.min(now % 12, 12).toFixed(2);
585
- els.scrubV.textContent = `${(now % 12).toFixed(2)}s`;
586
-
587
- els.readout.textContent =
588
- `t=${now.toFixed(2)}s
589
- beat=${beat.beatIndex + 1}/5 target=${target.target}${target.saccading ? ' (saccading)' : ''} sync=${beat.sync ? 'yes' : 'no'} ordered=${orderedCount}/4
590
- ${taskStates.map(s => `${s.key}: theta=${s.theta.toFixed(0)}° depth=${s.depth.toFixed(2)} slot=${s.slotMix.toFixed(2)} pos=(${s.p.x.toFixed(2)}, ${s.p.y.toFixed(2)})`).join('\n')}
591
- A/C 周期=${P.durA}s B/D 周期=${P.durB}s 比例=${(P.durB/P.durA).toFixed(3)}:1 点名循环=${(P.eyeSwitchT*5).toFixed(2)}s
592
- called=${calledState ? calledState.key : 'all'} blink=${blinkScale.toFixed(2)} closest=${closestPair.pair} ${closestPair.dist.toFixed(2)}u
593
- body: morph=${morph.toFixed(3)} call=${body.callPulse.toFixed(2)} sync=${body.syncPulse.toFixed(2)} tilt=${body.rot.toFixed(2)}°
594
- note: C 方向 = orbit 只做流动路径; conducting 读感靠眼神点名 + spark 响应 + 归位同步.`;
595
- }
596
-
597
- function gcd(a, b) { return b ? gcd(b, a % b) : a; }
598
-
599
- let startTime = performance.now();
600
- let scrubTime = 0;
601
-
602
- function loop(nowMs) {
603
- let now;
604
- if (P.pause) {
605
- now = scrubTime;
606
- } else {
607
- now = (nowMs - startTime) / 1000;
608
- scrubTime = now;
609
- }
610
- renderAt(now);
611
- requestAnimationFrame(loop);
612
- }
613
-
614
- requestAnimationFrame(loop);
615
- ]]></script>
616
- </svg>
1
+ <svg id="stageSvg" xmlns="http://www.w3.org/2000/svg" width="880" height="720" viewBox="-32 -24 88 72" preserveAspectRatio="xMidYMid meet">
2
+ <style><![CDATA[
3
+ #stageBg { fill: #0f0f14; }
4
+ .edge-rim-tight { stroke-opacity: .65; }
5
+ .edge-rim-soft { stroke-opacity: .50; }
6
+ .edge-contact { stroke-opacity: .65; }
7
+ .orbit-track { stroke: rgba(177,167,255,.13); stroke-width: 0.13; fill: none; stroke-dasharray: 0.45 0.65; }
8
+ .trail-spark { pointer-events: none; }
9
+ .spark-shell { stroke: rgba(255,255,255,.82); stroke-width: 0.075; stroke-linejoin: round; }
10
+ .spark-core { fill: #FFFDF0; opacity: .20; }
11
+ @media (prefers-color-scheme: light) {
12
+ #stageBg { fill: #fff; }
13
+ .edge-rim-tight { stroke-opacity: .75; }
14
+ .edge-rim-soft { stroke-opacity: .60; }
15
+ .edge-contact { stroke-opacity: .75; }
16
+ .cloud-drop-shadow { flood-opacity: .13; }
17
+ .orbit-track { stroke: rgba(57,65,255,.22); }
18
+ .spark-shell { stroke: rgba(255,255,255,.82); stroke-width: 0.075; }
19
+ .spark-core { opacity: .20; }
20
+ #taskTrailLayer use, #taskABack, #taskBBack, #taskCBack, #taskDBack, #taskAFront, #taskBFront, #taskCFront, #taskDFront { filter: url(#task-glow); }
21
+ .food-cloud-shadow { flood-opacity: .28; flood-color: #5060FF; }
22
+ .food-cloud > path {
23
+ stroke: #5870E8 !important;
24
+ stroke-width: 1.2 !important;
25
+ stroke-opacity: .9 !important;
26
+ vector-effect: non-scaling-stroke !important;
27
+ }
28
+ }
29
+ ]]></style>
30
+ <defs>
31
+ <linearGradient id="body-grad" x1="12" y1="2" x2="12" y2="22" gradientUnits="userSpaceOnUse">
32
+ <stop offset="0" stop-color="#f0f9ff"/>
33
+ <stop offset="0.5" stop-color="#e0f2fe"/>
34
+ <stop offset="1" stop-color="#bae6fd"/>
35
+ </linearGradient>
36
+ <linearGradient id="eye-grad" x1="12" y1="3" x2="12" y2="21" gradientUnits="userSpaceOnUse">
37
+ <stop offset="0" stop-color="#B1A7FF"/>
38
+ <stop offset="0.5" stop-color="#7A9DFF"/>
39
+ <stop offset="1" stop-color="#3941FF"/>
40
+ </linearGradient>
41
+ <radialGradient id="hi-grad" cx="0.45" cy="0.25" r="0.55">
42
+ <stop offset="0" stop-color="#f0f9ff" stop-opacity="0.9"/>
43
+ <stop offset="1" stop-color="#f0f9ff" stop-opacity="0"/>
44
+ </radialGradient>
45
+ <radialGradient id="taskA-grad" cx="0.48" cy="0.38" r="0.82">
46
+ <stop offset="0" stop-color="#FFF7D1"/>
47
+ <stop offset="0.58" stop-color="#FFD978"/>
48
+ <stop offset="1" stop-color="#DFA93C"/>
49
+ </radialGradient>
50
+ <radialGradient id="taskB-grad" cx="0.48" cy="0.38" r="0.82">
51
+ <stop offset="0" stop-color="#F7F0FF"/>
52
+ <stop offset="0.58" stop-color="#CDBBFF"/>
53
+ <stop offset="1" stop-color="#8F78D7"/>
54
+ </radialGradient>
55
+ <radialGradient id="taskC-grad" cx="0.48" cy="0.38" r="0.82">
56
+ <stop offset="0" stop-color="#F1FFFC"/>
57
+ <stop offset="0.58" stop-color="#9FE5DD"/>
58
+ <stop offset="1" stop-color="#50AFA6"/>
59
+ </radialGradient>
60
+ <radialGradient id="taskD-grad" cx="0.48" cy="0.38" r="0.82">
61
+ <stop offset="0" stop-color="#FFF3FA"/>
62
+ <stop offset="0.58" stop-color="#F7AFD4"/>
63
+ <stop offset="1" stop-color="#CE6FA6"/>
64
+ </radialGradient>
65
+ <filter id="cloud-drop" x="-45%" y="-45%" width="190%" height="190%">
66
+ <feDropShadow class="cloud-drop-shadow" dx="0" dy="0.6" stdDeviation="0.8" flood-color="#3941FF" flood-opacity="0.18"/>
67
+ </filter>
68
+ <filter id="edge-blur" x="-40%" y="-40%" width="180%" height="180%">
69
+ <feGaussianBlur stdDeviation="0.16"/>
70
+ </filter>
71
+ <filter id="task-glow" x="-85%" y="-85%" width="270%" height="270%">
72
+ <feGaussianBlur stdDeviation="0.16" result="b"/>
73
+ <feMerge>
74
+ <feMergeNode in="b"/>
75
+ <feMergeNode in="SourceGraphic"/>
76
+ </feMerge>
77
+ </filter>
78
+ <filter id="task-light-glow" x="-90%" y="-90%" width="280%" height="280%">
79
+ <feDropShadow dx="0" dy="0.06" stdDeviation="0.18" flood-color="#4A4E76" flood-opacity="0.30"/>
80
+ <feGaussianBlur stdDeviation="0.08" result="b"/>
81
+ <feMerge>
82
+ <feMergeNode in="b"/>
83
+ <feMergeNode in="SourceGraphic"/>
84
+ </feMerge>
85
+ </filter>
86
+ <clipPath id="cloud-clip">
87
+ <path id="cloud-clip-path"/>
88
+ </clipPath>
89
+ <symbol id="taskA-symbol" viewBox="-1.22 -1.22 2.44 2.44">
90
+ <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
91
+ fill="url(#taskA-grad)" opacity="0.98"/>
92
+ <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
93
+ class="spark-core"/>
94
+ </symbol>
95
+ <symbol id="taskB-symbol" viewBox="-1.22 -1.22 2.44 2.44">
96
+ <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
97
+ fill="url(#taskB-grad)" opacity="0.98"/>
98
+ <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
99
+ class="spark-core"/>
100
+ </symbol>
101
+ <symbol id="taskC-symbol" viewBox="-1.22 -1.22 2.44 2.44">
102
+ <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
103
+ fill="url(#taskC-grad)" opacity="0.98"/>
104
+ <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
105
+ class="spark-core"/>
106
+ </symbol>
107
+ <symbol id="taskD-symbol" viewBox="-1.22 -1.22 2.44 2.44">
108
+ <path class="spark-shell" d="M 0 -1.06 C 0.15 -0.47 0.34 -0.20 0.98 -0.02 C 0.36 0.12 0.14 0.34 0 1.04 C -0.14 0.34 -0.36 0.12 -0.98 -0.02 C -0.34 -0.20 -0.15 -0.47 0 -1.06 Z"
109
+ fill="url(#taskD-grad)" opacity="0.98"/>
110
+ <path d="M 0 -0.48 C 0.07 -0.19 0.18 -0.08 0.42 -0.01 C 0.18 0.06 0.07 0.18 0 0.48 C -0.07 0.18 -0.18 0.06 -0.42 -0.01 C -0.18 -0.08 -0.07 -0.19 0 -0.48 Z"
111
+ class="spark-core"/>
112
+ </symbol>
113
+ </defs>
114
+ <g id="orbitTrackLayer">
115
+ <path id="orbitTrackA" class="orbit-track"/>
116
+ <path id="orbitTrackB" class="orbit-track"/>
117
+ </g>
118
+
119
+ <g id="taskTrailLayer">
120
+ <g id="trailA" class="trail-spark">
121
+ <use id="trailA0" href="#taskA-symbol" filter="url(#task-glow)"/>
122
+ <use id="trailA1" href="#taskA-symbol" filter="url(#task-glow)"/>
123
+ <use id="trailA2" href="#taskA-symbol" filter="url(#task-glow)"/>
124
+ <use id="trailA3" href="#taskA-symbol" filter="url(#task-glow)"/>
125
+ </g>
126
+ <g id="trailB" class="trail-spark">
127
+ <use id="trailB0" href="#taskB-symbol" filter="url(#task-glow)"/>
128
+ <use id="trailB1" href="#taskB-symbol" filter="url(#task-glow)"/>
129
+ <use id="trailB2" href="#taskB-symbol" filter="url(#task-glow)"/>
130
+ <use id="trailB3" href="#taskB-symbol" filter="url(#task-glow)"/>
131
+ </g>
132
+ <g id="trailC" class="trail-spark">
133
+ <use id="trailC0" href="#taskC-symbol" filter="url(#task-glow)"/>
134
+ <use id="trailC1" href="#taskC-symbol" filter="url(#task-glow)"/>
135
+ <use id="trailC2" href="#taskC-symbol" filter="url(#task-glow)"/>
136
+ <use id="trailC3" href="#taskC-symbol" filter="url(#task-glow)"/>
137
+ </g>
138
+ <g id="trailD" class="trail-spark">
139
+ <use id="trailD0" href="#taskD-symbol" filter="url(#task-glow)"/>
140
+ <use id="trailD1" href="#taskD-symbol" filter="url(#task-glow)"/>
141
+ <use id="trailD2" href="#taskD-symbol" filter="url(#task-glow)"/>
142
+ <use id="trailD3" href="#taskD-symbol" filter="url(#task-glow)"/>
143
+ </g>
144
+ </g>
145
+
146
+ <use id="taskABack" href="#taskA-symbol" filter="url(#task-glow)"/>
147
+ <use id="taskBBack" href="#taskB-symbol" filter="url(#task-glow)"/>
148
+ <use id="taskCBack" href="#taskC-symbol" filter="url(#task-glow)"/>
149
+ <use id="taskDBack" href="#taskD-symbol" filter="url(#task-glow)"/>
150
+
151
+ <g id="cloudRoot">
152
+ <g id="cloudEdge">
153
+ <path id="edgeContact" class="edge-contact" fill="none" stroke="#7d8aa7" stroke-width="1.05" stroke-linejoin="round" transform="translate(0 0.18)" filter="url(#edge-blur)"/>
154
+ <path id="edgeRimSoft" class="edge-rim-soft" fill="none" stroke="#7A9DFF" stroke-width="0.90" stroke-linejoin="round" filter="url(#edge-blur)"/>
155
+ <path id="edgeRimTight" class="edge-rim-tight" fill="none" stroke="#aab3c6" stroke-width="0.42" stroke-linejoin="round"/>
156
+ </g>
157
+ <g id="cloudGroup" filter="url(#cloud-drop)">
158
+ <path id="cloudPath" fill="url(#body-grad)"/>
159
+ <rect x="1" y="2" width="22" height="11" fill="url(#hi-grad)" clip-path="url(#cloud-clip)"/>
160
+ </g>
161
+ <g id="eyeGroup">
162
+ <rect id="eyeL" x="7.7" y="9.5" width="2.6" height="5" rx="1.3" ry="1.3" fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7"/>
163
+ <rect id="eyeR" x="13.7" y="9.5" width="2.6" height="5" rx="1.3" ry="1.3" fill="url(#eye-grad)" stroke="#ffffff" stroke-width="0.08" stroke-opacity="0.7"/>
164
+ </g>
165
+ </g>
166
+
167
+ <use id="taskAFront" href="#taskA-symbol" filter="url(#task-glow)"/>
168
+ <use id="taskBFront" href="#taskB-symbol" filter="url(#task-glow)"/>
169
+ <use id="taskCFront" href="#taskC-symbol" filter="url(#task-glow)"/>
170
+ <use id="taskDFront" href="#taskD-symbol" filter="url(#task-glow)"/>
171
+ <g id="__cloudling-export-shims" opacity="0" pointer-events="none" style="display:none">
172
+ <g id="readout"></g>
173
+ <g id="scrub"></g>
174
+ <g id="scrubV"></g>
175
+ </g>
176
+
177
+ <script type="application/ecmascript"><![CDATA[
178
+ const CLOUD_D = "M9.064 3.344a4.578 4.578 0 012.285-.312c1 .115 1.891.54 2.673 1.275.01.01.024.017.037.021a.09.09 0 00.043 0 4.55 4.55 0 013.046.275l.047.022.116.057a4.581 4.581 0 012.188 2.399c.209.51.313 1.041.315 1.595a4.24 4.24 0 01-.134 1.223.123.123 0 00.03.115c.594.607.988 1.33 1.183 2.17.289 1.425-.007 2.71-.887 3.854l-.136.166a4.548 4.548 0 01-2.201 1.388.123.123 0 00-.081.076c-.191.551-.383 1.023-.74 1.494-.9 1.187-2.222 1.846-3.711 1.838-1.187-.006-2.239-.44-3.157-1.302a.107.107 0 00-.105-.024c-.388.125-.78.143-1.204.138a4.441 4.441 0 01-1.945-.466 4.544 4.544 0 01-1.61-1.335c-.152-.202-.303-.392-.414-.617a5.81 5.81 0 01-.37-.961 4.582 4.582 0 01-.014-2.298.124.124 0 00.006-.056.085.085 0 00-.027-.048 4.467 4.467 0 01-1.034-1.651 3.896 3.896 0 01-.251-1.192 5.189 5.189 0 01.141-1.6c.337-1.112.982-1.985 1.933-2.618.212-.141.413-.251.601-.33.215-.089.43-.164.646-.227a.098.098 0 00.065-.066 4.51 4.51 0 01.829-1.615 4.535 4.535 0 011.837-1.388z";
179
+
180
+ const CENTER = { x: 12, y: 12 };
181
+
182
+ const els = {
183
+ cloudRoot: document.getElementById('cloudRoot'),
184
+ cloudPath: document.getElementById('cloudPath'),
185
+ cloudClipPath: document.getElementById('cloud-clip-path'),
186
+ edgeContact: document.getElementById('edgeContact'),
187
+ edgeRimSoft: document.getElementById('edgeRimSoft'),
188
+ edgeRimTight: document.getElementById('edgeRimTight'),
189
+ eyeGroup: document.getElementById('eyeGroup'),
190
+ eyeL: document.getElementById('eyeL'),
191
+ eyeR: document.getElementById('eyeR'),
192
+ eyeGrad: document.getElementById('eye-grad'),
193
+ taskAFront: document.getElementById('taskAFront'),
194
+ taskABack: document.getElementById('taskABack'),
195
+ taskBFront: document.getElementById('taskBFront'),
196
+ taskBBack: document.getElementById('taskBBack'),
197
+ taskCFront: document.getElementById('taskCFront'),
198
+ taskCBack: document.getElementById('taskCBack'),
199
+ taskDFront: document.getElementById('taskDFront'),
200
+ taskDBack: document.getElementById('taskDBack'),
201
+ trailA: document.getElementById('trailA'),
202
+ trailA0: document.getElementById('trailA0'),
203
+ trailA1: document.getElementById('trailA1'),
204
+ trailA2: document.getElementById('trailA2'),
205
+ trailA3: document.getElementById('trailA3'),
206
+ trailB: document.getElementById('trailB'),
207
+ trailB0: document.getElementById('trailB0'),
208
+ trailB1: document.getElementById('trailB1'),
209
+ trailB2: document.getElementById('trailB2'),
210
+ trailB3: document.getElementById('trailB3'),
211
+ trailC: document.getElementById('trailC'),
212
+ trailC0: document.getElementById('trailC0'),
213
+ trailC1: document.getElementById('trailC1'),
214
+ trailC2: document.getElementById('trailC2'),
215
+ trailC3: document.getElementById('trailC3'),
216
+ trailD: document.getElementById('trailD'),
217
+ trailD0: document.getElementById('trailD0'),
218
+ trailD1: document.getElementById('trailD1'),
219
+ trailD2: document.getElementById('trailD2'),
220
+ trailD3: document.getElementById('trailD3'),
221
+ orbitTrackA: document.getElementById('orbitTrackA'),
222
+ orbitTrackB: document.getElementById('orbitTrackB'),
223
+ orbitTrackLayer: document.getElementById('orbitTrackLayer'),
224
+ readout: document.getElementById('readout'),
225
+ scrub: document.getElementById('scrub'),
226
+ scrubV: document.getElementById('scrubV')
227
+ };
228
+
229
+ const P = {
230
+ durA: 4.0, rxA: 14, ryA: 4, tiltA: -10, theta0A: 0,
231
+ durB: 6.0, rxB: 4, ryB: 11, tiltB: 20, theta0B: 180,
232
+ starSize: 1.75,
233
+ backScale: 0.74,
234
+ backOpacity: 0.28,
235
+ depthSnap: 0.16,
236
+ starSpin: 65,
237
+ slotRadius: 14.0,
238
+ slotSquash: 0.96,
239
+ slotRot: 0,
240
+ slotAX: 0, slotAY: 0,
241
+ slotBX: 0, slotBY: 0,
242
+ slotCX: 0, slotCY: 0,
243
+ slotDX: 0, slotDY: 0,
244
+ bodyBreath: 0.022,
245
+ bodyFloat: 0.16,
246
+ bodyLean: 0.48,
247
+ bodyTilt: 1.15,
248
+ callMorph: 0.075,
249
+ syncMorph: 0.16,
250
+ morphAmt: 0.04,
251
+ morphPeriod: 5.0,
252
+ cloudR: 7.5,
253
+ eyeShift: 1.10,
254
+ eyeSwitchT: 1.15,
255
+ saccade: 0.13,
256
+ eyeFlowPeriod: 5.5,
257
+ eyeFlowAmp: 4,
258
+ blinkInterval: 6.0,
259
+ blinkDur: 0.16,
260
+ showTracks: false,
261
+ pause: false
262
+ };
263
+
264
+ const lerp = (a, b, t) => a + (b - a) * t;
265
+ const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
266
+ const smoothstep = t => t * t * (3 - 2 * t);
267
+
268
+ const EYE_BASE = {
269
+ l: { y: 9.5, h: 5, rx: 1.3 },
270
+ r: { y: 9.5, h: 5, rx: 1.3 }
271
+ };
272
+
273
+ const TASK_ORDER = ['A', 'B', 'C', 'D'];
274
+ const TASKS = [
275
+ { key: 'A', orbit: 'A', phase: 8, slotAngle: 235, spin: 0.35, front: els.taskAFront, back: els.taskABack, trail: [els.trailA0, els.trailA1, els.trailA2, els.trailA3] },
276
+ { key: 'B', orbit: 'B', phase: 72, slotAngle: 324, spin: -0.28, front: els.taskBFront, back: els.taskBBack, trail: [els.trailB0, els.trailB1, els.trailB2, els.trailB3] },
277
+ { key: 'C', orbit: 'A', phase: 145, slotAngle: 53, spin: 0.30, front: els.taskCFront, back: els.taskCBack, trail: [els.trailC0, els.trailC1, els.trailC2, els.trailC3] },
278
+ { key: 'D', orbit: 'B', phase: 252, slotAngle: 142, spin: -0.32, front: els.taskDFront, back: els.taskDBack, trail: [els.trailD0, els.trailD1, els.trailD2, els.trailD3] }
279
+ ];
280
+
281
+ function samplePath(d, N) {
282
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
283
+ path.setAttribute('d', d);
284
+ const pts = [];
285
+ const len = path.getTotalLength();
286
+ for (let i = 0; i < N; i++) {
287
+ const p = path.getPointAtLength(i / N * len);
288
+ pts.push({ x: p.x, y: p.y });
289
+ }
290
+ return pts;
291
+ }
292
+
293
+ const basePoints = samplePath(CLOUD_D, 64);
294
+
295
+ function morphPoints(pts, R, t) {
296
+ return pts.map(p => {
297
+ const dx = p.x - CENTER.x;
298
+ const dy = p.y - CENTER.y;
299
+ const a = Math.atan2(dy, dx);
300
+ return {
301
+ x: lerp(p.x, CENTER.x + R * Math.cos(a), t),
302
+ y: lerp(p.y, CENTER.y + R * Math.sin(a), t)
303
+ };
304
+ });
305
+ }
306
+
307
+ function pointsToSmoothPath(pts) {
308
+ const n = pts.length;
309
+ let d = `M ${pts[0].x.toFixed(3)} ${pts[0].y.toFixed(3)}`;
310
+ for (let i = 0; i < n; i++) {
311
+ const p0 = pts[(i - 1 + n) % n];
312
+ const p1 = pts[i];
313
+ const p2 = pts[(i + 1) % n];
314
+ const p3 = pts[(i + 2) % n];
315
+ const c1x = p1.x + (p2.x - p0.x) / 6;
316
+ const c1y = p1.y + (p2.y - p0.y) / 6;
317
+ const c2x = p2.x - (p3.x - p1.x) / 6;
318
+ const c2y = p2.y - (p3.y - p1.y) / 6;
319
+ d += ` C ${c1x.toFixed(3)} ${c1y.toFixed(3)} ${c2x.toFixed(3)} ${c2y.toFixed(3)} ${p2.x.toFixed(3)} ${p2.y.toFixed(3)}`;
320
+ }
321
+ return d + ' Z';
322
+ }
323
+
324
+ function orbitPoint(thetaDeg, rx, ry, tiltDeg) {
325
+ const t = thetaDeg * Math.PI / 180;
326
+ const r = tiltDeg * Math.PI / 180;
327
+ const ct = Math.cos(t), st = Math.sin(t);
328
+ const cr = Math.cos(r), sr = Math.sin(r);
329
+ return {
330
+ x: CENTER.x + rx * ct * cr - ry * st * sr,
331
+ y: CENTER.y + rx * ct * sr + ry * st * cr
332
+ };
333
+ }
334
+
335
+ function orbitTrackD(rx, ry, tiltDeg, samples) {
336
+ let d = '';
337
+ for (let i = 0; i <= samples; i++) {
338
+ const theta = i / samples * 360;
339
+ const p = orbitPoint(theta, rx, ry, tiltDeg);
340
+ d += i === 0 ? `M ${p.x.toFixed(3)} ${p.y.toFixed(3)}` : ` L ${p.x.toFixed(3)} ${p.y.toFixed(3)}`;
341
+ }
342
+ return d + ' Z';
343
+ }
344
+
345
+ function getBeat(now) {
346
+ const beatT = Math.max(P.eyeSwitchT, 0.1);
347
+ const cycleT = beatT * 5;
348
+ const cyclePos = (now % cycleT) / beatT;
349
+ const beatIndex = Math.floor(cyclePos);
350
+ const beatPhase = cyclePos - beatIndex;
351
+ return {
352
+ beatT,
353
+ cyclePos,
354
+ beatIndex,
355
+ beatPhase,
356
+ target: beatIndex < TASK_ORDER.length ? TASK_ORDER[beatIndex] : 'SYNC',
357
+ sync: beatIndex >= TASK_ORDER.length
358
+ };
359
+ }
360
+
361
+ function getOrbitParams(task) {
362
+ return task.orbit === 'A'
363
+ ? { dur: P.durA, theta0: P.theta0A, rx: P.rxA, ry: P.ryA, tilt: P.tiltA }
364
+ : { dur: P.durB, theta0: P.theta0B, rx: P.rxB, ry: P.ryB, tilt: P.tiltB };
365
+ }
366
+
367
+ function getTaskSlot(task) {
368
+ const a = (task.slotAngle + P.slotRot) * Math.PI / 180;
369
+ return {
370
+ x: CENTER.x + Math.cos(a) * P.slotRadius + P[`slot${task.key}X`],
371
+ y: CENTER.y + Math.sin(a) * P.slotRadius * P.slotSquash + P[`slot${task.key}Y`]
372
+ };
373
+ }
374
+
375
+ function slotMixForTask(task, beat) {
376
+ const idx = TASK_ORDER.indexOf(task.key);
377
+ const callLocal = beat.cyclePos - idx;
378
+ if (callLocal < 0 || callLocal > 4.88) return 0;
379
+ const arrive = smoothstep(clamp((callLocal - 0.04) / 0.22, 0, 1));
380
+ const release = beat.cyclePos > 4.55 ? 1 - smoothstep(clamp((beat.cyclePos - 4.55) / 0.33, 0, 1)) : 1;
381
+ return arrive * release;
382
+ }
383
+
384
+ function getTaskState(now, task, beat) {
385
+ const p = getOrbitParams(task);
386
+ const progress = (now / p.dur) % 1;
387
+ const theta = p.theta0 + task.phase + progress * 360;
388
+ const depth0 = Math.sin(theta * Math.PI / 180);
389
+ const orbitPos = orbitPoint(theta, p.rx, p.ry, p.tilt);
390
+ const slot = getTaskSlot(task);
391
+ const slotMix = slotMixForTask(task, beat);
392
+ const pos = {
393
+ x: lerp(orbitPos.x, slot.x, slotMix),
394
+ y: lerp(orbitPos.y, slot.y, slotMix)
395
+ };
396
+ const called = beat.target === task.key;
397
+ const callPulse = called ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
398
+ const syncPulse = beat.sync ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
399
+ const frontLift = Math.max(slotMix, callPulse, syncPulse * 0.48);
400
+ const depth = lerp(depth0, 0.68, frontLift);
401
+ const scaleBoost = 1 + callPulse * 0.14 + syncPulse * 0.10 + slotMix * 0.04;
402
+ const glowBoost = callPulse * 0.24 + syncPulse * 0.20 + slotMix * 0.10;
403
+ return { ...task, p: pos, orbitPos, slot, depth, theta, progress, slotMix, called, callPulse, syncPulse, scaleBoost, glowBoost };
404
+ }
405
+
406
+ function setTaskLayer(state, baseSize, spinDeg) {
407
+ const snap = P.depthSnap;
408
+ const depthFrontOp = smoothstep(clamp((state.depth - snap) / (0.58 - snap), 0, 1));
409
+ const frontOp = Math.max(depthFrontOp, state.callPulse * 0.72, state.syncPulse * 0.58);
410
+ const baseBack = state.depth < snap ? P.backOpacity : P.backOpacity * (1 - frontOp);
411
+ const frontBoost = 1 + state.glowBoost;
412
+ const backBoost = 1 + state.glowBoost * 0.45;
413
+ const sizeFront = baseSize * state.scaleBoost;
414
+ const sizeBack = baseSize * P.backScale * (0.96 + state.scaleBoost * 0.04);
415
+ const setUse = (el, sz, op) => {
416
+ el.setAttribute('x', (-sz).toFixed(3));
417
+ el.setAttribute('y', (-sz).toFixed(3));
418
+ el.setAttribute('width', (2 * sz).toFixed(3));
419
+ el.setAttribute('height', (2 * sz).toFixed(3));
420
+ el.setAttribute('transform', `translate(${state.p.x.toFixed(3)} ${state.p.y.toFixed(3)}) rotate(${spinDeg.toFixed(2)})`);
421
+ el.setAttribute('opacity', op.toFixed(3));
422
+ };
423
+ setUse(state.front, sizeFront, clamp(frontOp * frontBoost, 0, 1));
424
+ setUse(state.back, sizeBack, clamp(baseBack * backBoost, 0, 0.8));
425
+ }
426
+
427
+ function taskTrailPoint(state, index) {
428
+ const p = getOrbitParams(state);
429
+ return orbitPoint(state.theta - 8 - index * 6.5, p.rx, p.ry, p.tilt);
430
+ }
431
+
432
+ function setTaskTrail(state) {
433
+ const depthPresence = 0.52 + Math.max(state.depth, 0) * 0.35;
434
+ const baseOp = (1 - state.slotMix) * depthPresence;
435
+ const trailOps = [0.17, 0.09, 0.05, 0.025];
436
+ const trailSizes = [0.28, 0.20, 0.14, 0.09];
437
+ state.trail.forEach((el, i) => {
438
+ const pos = taskTrailPoint(state, i);
439
+ const sz = P.starSize * trailSizes[i];
440
+ el.setAttribute('x', (-sz).toFixed(3));
441
+ el.setAttribute('y', (-sz).toFixed(3));
442
+ el.setAttribute('width', (2 * sz).toFixed(3));
443
+ el.setAttribute('height', (2 * sz).toFixed(3));
444
+ el.setAttribute('transform', `translate(${pos.x.toFixed(3)} ${pos.y.toFixed(3)}) rotate(${(state.theta * 0.28 + i * 17).toFixed(2)})`);
445
+ el.setAttribute('opacity', clamp(baseOp * trailOps[i], 0, 0.3).toFixed(3));
446
+ });
447
+ }
448
+
449
+ let nextBlinkT = 2.4;
450
+ let blinkActive = false;
451
+ let blinkStartT = 0;
452
+ function getBlinkScale(t) {
453
+ if (blinkActive && t < blinkStartT) {
454
+ blinkActive = false;
455
+ nextBlinkT = t + 2.4;
456
+ return 1;
457
+ }
458
+ if (!blinkActive && t > nextBlinkT) {
459
+ blinkActive = true;
460
+ blinkStartT = t;
461
+ }
462
+ if (blinkActive) {
463
+ const dt = t - blinkStartT;
464
+ const half = P.blinkDur / 2;
465
+ if (dt < half) return 1 - 0.94 * (dt / half);
466
+ if (dt < P.blinkDur) return 0.06 + 0.94 * ((dt - half) / half);
467
+ blinkActive = false;
468
+ nextBlinkT = t + P.blinkInterval * (0.82 + Math.random() * 0.36);
469
+ }
470
+ return 1;
471
+ }
472
+
473
+ function applyBlink(rect, base, scale) {
474
+ const h = Math.max(base.h * scale, 0.15);
475
+ const y = base.y + (base.h - h) / 2;
476
+ const rx = Math.min(base.rx, h / 2);
477
+ rect.setAttribute('height', h.toFixed(3));
478
+ rect.setAttribute('y', y.toFixed(3));
479
+ rect.setAttribute('rx', rx.toFixed(3));
480
+ rect.setAttribute('ry', rx.toFixed(3));
481
+ }
482
+
483
+ function getTargetPoint(key, states) {
484
+ if (key === 'SYNC') return { x: CENTER.x, y: CENTER.y - 2.2 };
485
+ return states.find(s => s.key === key)?.p || CENTER;
486
+ }
487
+
488
+ function getEyeTarget(beat, states) {
489
+ const target = beat.target;
490
+ const prevKey = beat.beatIndex === 0 ? 'SYNC' : TASK_ORDER[beat.beatIndex - 1];
491
+ const oldPos = getTargetPoint(prevKey, states);
492
+ const newPos = getTargetPoint(target, states);
493
+ const blendT = clamp((beat.beatPhase * beat.beatT) / P.saccade, 0, 1);
494
+ const eased = smoothstep(blendT);
495
+ return {
496
+ x: lerp(oldPos.x, newPos.x, eased),
497
+ y: lerp(oldPos.y, newPos.y, eased),
498
+ target,
499
+ saccading: blendT < 1
500
+ };
501
+ }
502
+
503
+ function getBodyResponse(now, beat, states) {
504
+ const called = states.find(s => s.key === beat.target);
505
+ const syncPulse = beat.sync ? Math.sin(Math.PI * clamp(beat.beatPhase, 0, 1)) : 0;
506
+ const callPulse = called ? called.callPulse : 0;
507
+ const focus = called ? called.p : { x: CENTER.x, y: CENTER.y - 2.4 };
508
+ const dx = focus.x - CENTER.x;
509
+ const dy = focus.y - CENTER.y;
510
+ const len = Math.max(Math.hypot(dx, dy), 0.001);
511
+ const nx = dx / len;
512
+ const ny = dy / len;
513
+ const breath = Math.sin(2 * Math.PI * now / 3.8);
514
+ const softFloat = Math.sin(2 * Math.PI * now / 4.6 - 0.4);
515
+ const idleTilt = Math.sin(2 * Math.PI * now / 5.2 + 0.7) * 0.42;
516
+ const lean = callPulse * P.bodyLean;
517
+ const tx = nx * lean;
518
+ const ty = ny * lean * 0.48 + softFloat * P.bodyFloat;
519
+ const rot = idleTilt + nx * callPulse * P.bodyTilt + syncPulse * 0.35 * Math.sin(2 * Math.PI * now / 0.9);
520
+ const sx = 1 + breath * P.bodyBreath * 0.62 + callPulse * 0.012 + syncPulse * 0.026;
521
+ const sy = 1 + breath * P.bodyBreath - callPulse * 0.006 + syncPulse * 0.020;
522
+ const morphAdd = callPulse * P.callMorph + syncPulse * P.syncMorph;
523
+ return { tx, ty, rot, sx, sy, callPulse, syncPulse, morphAdd };
524
+ }
525
+
526
+ function setCloudRootTransform(body) {
527
+ els.cloudRoot.setAttribute(
528
+ 'transform',
529
+ `translate(${(CENTER.x + body.tx).toFixed(3)} ${(CENTER.y + body.ty).toFixed(3)}) rotate(${body.rot.toFixed(3)}) scale(${body.sx.toFixed(4)} ${body.sy.toFixed(4)}) translate(${-CENTER.x} ${-CENTER.y})`
530
+ );
531
+ }
532
+
533
+ function renderAt(now) {
534
+ const beat = getBeat(now);
535
+ const taskStates = TASKS.map(task => getTaskState(now, task, beat));
536
+ const body = getBodyResponse(now, beat, taskStates);
537
+ setCloudRootTransform(body);
538
+
539
+ const morphPhase = (now / P.morphPeriod) * 2 * Math.PI;
540
+ const morph = clamp(((Math.sin(morphPhase) * 0.5 + 0.5) * P.morphAmt) + body.morphAdd, 0, 0.32);
541
+ const cloudD = pointsToSmoothPath(morphPoints(basePoints, P.cloudR, morph));
542
+ els.cloudPath.setAttribute('d', cloudD);
543
+ els.cloudClipPath.setAttribute('d', cloudD);
544
+ els.edgeContact.setAttribute('d', cloudD);
545
+ els.edgeRimSoft.setAttribute('d', cloudD);
546
+ els.edgeRimTight.setAttribute('d', cloudD);
547
+
548
+ els.orbitTrackA.setAttribute('d', orbitTrackD(P.rxA, P.ryA, P.tiltA, 96));
549
+ els.orbitTrackB.setAttribute('d', orbitTrackD(P.rxB, P.ryB, P.tiltB, 96));
550
+ els.orbitTrackLayer.setAttribute('opacity', P.showTracks ? 1 : 0);
551
+
552
+ const spin = (now * P.starSpin) % 360;
553
+ taskStates.forEach(state => {
554
+ setTaskTrail(state);
555
+ setTaskLayer(state, P.starSize, spin * state.spin);
556
+ });
557
+
558
+ const target = getEyeTarget(beat, taskStates);
559
+ const lookDx = target.x - CENTER.x;
560
+ const lookDy = target.y - CENTER.y;
561
+ const lookLen = Math.max(Math.hypot(lookDx, lookDy), 0.001);
562
+ const eyeX = lookDx / lookLen * P.eyeShift;
563
+ const eyeY = lookDy / lookLen * P.eyeShift * 0.55;
564
+ els.eyeGroup.setAttribute('transform', `translate(${eyeX.toFixed(3)} ${eyeY.toFixed(3)})`);
565
+
566
+ const blinkScale = getBlinkScale(now);
567
+ applyBlink(els.eyeL, EYE_BASE.l, blinkScale);
568
+ applyBlink(els.eyeR, EYE_BASE.r, blinkScale);
569
+ const flowOffset = Math.sin(2 * Math.PI * now / P.eyeFlowPeriod) * P.eyeFlowAmp;
570
+ els.eyeGrad.setAttribute('y1', (3 + flowOffset).toFixed(2));
571
+ els.eyeGrad.setAttribute('y2', (21 + flowOffset).toFixed(2));
572
+
573
+ const calledState = taskStates.find(s => s.key === target.target);
574
+ const orderedCount = taskStates.filter(s => s.slotMix > 0.82).length;
575
+ const closestPair = taskStates.reduce((best, a, i) => {
576
+ for (let j = i + 1; j < taskStates.length; j++) {
577
+ const b = taskStates[j];
578
+ const dist = Math.hypot(a.p.x - b.p.x, a.p.y - b.p.y);
579
+ if (dist < best.dist) best = { dist, pair: `${a.key}${b.key}` };
580
+ }
581
+ return best;
582
+ }, { dist: Infinity, pair: '--' });
583
+
584
+ els.scrub.value = Math.min(now % 12, 12).toFixed(2);
585
+ els.scrubV.textContent = `${(now % 12).toFixed(2)}s`;
586
+
587
+ els.readout.textContent =
588
+ `t=${now.toFixed(2)}s
589
+ beat=${beat.beatIndex + 1}/5 target=${target.target}${target.saccading ? ' (saccading)' : ''} sync=${beat.sync ? 'yes' : 'no'} ordered=${orderedCount}/4
590
+ ${taskStates.map(s => `${s.key}: theta=${s.theta.toFixed(0)}° depth=${s.depth.toFixed(2)} slot=${s.slotMix.toFixed(2)} pos=(${s.p.x.toFixed(2)}, ${s.p.y.toFixed(2)})`).join('\n')}
591
+ A/C 周期=${P.durA}s B/D 周期=${P.durB}s 比例=${(P.durB/P.durA).toFixed(3)}:1 点名循环=${(P.eyeSwitchT*5).toFixed(2)}s
592
+ called=${calledState ? calledState.key : 'all'} blink=${blinkScale.toFixed(2)} closest=${closestPair.pair} ${closestPair.dist.toFixed(2)}u
593
+ body: morph=${morph.toFixed(3)} call=${body.callPulse.toFixed(2)} sync=${body.syncPulse.toFixed(2)} tilt=${body.rot.toFixed(2)}°
594
+ note: C 方向 = orbit 只做流动路径; conducting 读感靠眼神点名 + spark 响应 + 归位同步.`;
595
+ }
596
+
597
+ function gcd(a, b) { return b ? gcd(b, a % b) : a; }
598
+
599
+ let startTime = performance.now();
600
+ let scrubTime = 0;
601
+
602
+ function loop(nowMs) {
603
+ let now;
604
+ if (P.pause) {
605
+ now = scrubTime;
606
+ } else {
607
+ now = (nowMs - startTime) / 1000;
608
+ scrubTime = now;
609
+ }
610
+ renderAt(now);
611
+ requestAnimationFrame(loop);
612
+ }
613
+
614
+ requestAnimationFrame(loop);
615
+ ]]></script>
616
+ </svg>