@mirage-engine/painter 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @mirage-engine/painter
2
2
 
3
+ ## 1.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - e2479a9: shadow animation
8
+
9
+ ## 1.0.5
10
+
11
+ ### Patch Changes
12
+
13
+ - ed5f57f: provide box shadow
14
+
3
15
  ## 1.0.4
4
16
 
5
17
  ### Patch Changes
@@ -1,8 +1,8 @@
1
1
  var k = Object.defineProperty;
2
- var P = (r, e, t) => e in r ? k(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
- var S = (r, e, t) => (P(r, typeof e != "symbol" ? e + "" : e, t), t);
4
- import * as d from "three";
5
- class A {
2
+ var G = (r, e, n) => e in r ? k(r, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : r[e] = n;
3
+ var S = (r, e, n) => (G(r, typeof e != "symbol" ? e + "" : e, n), n);
4
+ import * as l from "three";
5
+ class I {
6
6
  /**
7
7
  * Calculates or retrieves the exact alphabetic baseline (in pixels) for the given font.
8
8
  * @param font CSS font shorthand string (e.g., "400 16px Inter")
@@ -11,24 +11,24 @@ class A {
11
11
  static getBaseline(e) {
12
12
  if (this.cache.has(e))
13
13
  return this.cache.get(e);
14
- const t = this.calculateBaselineFromDOM(e);
15
- return this.cache.set(e, t), t;
14
+ const n = this.calculateBaselineFromDOM(e);
15
+ return this.cache.set(e, n), n;
16
16
  }
17
17
  static calculateBaselineFromDOM(e) {
18
18
  if (typeof document > "u")
19
19
  return 0;
20
- const t = document.createElement("div"), n = document.createElement("span"), o = document.createElement("img");
21
- t.style.visibility = "hidden", t.style.position = "absolute", t.style.top = "0px", t.style.left = "0px", t.style.font = e, t.style.margin = "0", t.style.padding = "0", t.style.border = "none", n.style.margin = "0", n.style.padding = "0", n.style.border = "none", n.style.lineHeight = "normal", o.width = 1, o.height = 1, o.style.verticalAlign = "baseline", n.appendChild(document.createTextNode("Hidden Text")), t.appendChild(n), t.appendChild(o), document.body.appendChild(t);
22
- const a = o.offsetTop - n.offsetTop;
23
- return document.body.removeChild(t), a;
20
+ const n = document.createElement("div"), i = document.createElement("span"), o = document.createElement("img");
21
+ n.style.visibility = "hidden", n.style.position = "absolute", n.style.top = "0px", n.style.left = "0px", n.style.font = e, n.style.margin = "0", n.style.padding = "0", n.style.border = "none", i.style.margin = "0", i.style.padding = "0", i.style.border = "none", i.style.lineHeight = "normal", o.width = 1, o.height = 1, o.style.verticalAlign = "baseline", i.appendChild(document.createTextNode("Hidden Text")), n.appendChild(i), n.appendChild(o), document.body.appendChild(n);
22
+ const a = o.offsetTop - i.offsetTop;
23
+ return document.body.removeChild(n), a;
24
24
  }
25
25
  }
26
- S(A, "cache", /* @__PURE__ */ new Map());
27
- class G extends d.MeshBasicMaterial {
28
- constructor(t, n, o, a, i = 2) {
26
+ S(I, "cache", /* @__PURE__ */ new Map());
27
+ class L extends l.MeshBasicMaterial {
28
+ constructor(n, i, o, a, t = 2) {
29
29
  super({
30
30
  transparent: !0,
31
- side: d.FrontSide,
31
+ side: l.FrontSide,
32
32
  color: 16777215
33
33
  });
34
34
  S(this, "canvas");
@@ -36,34 +36,34 @@ class G extends d.MeshBasicMaterial {
36
36
  S(this, "qualityFactor");
37
37
  if (this.canvas = document.createElement("canvas"), this.ctx = this.canvas.getContext("2d"), !this.ctx)
38
38
  throw new Error("[Mirage] Failed to create canvas context");
39
- this.qualityFactor = i, this.map = new d.CanvasTexture(this.canvas), this.map.colorSpace = d.LinearSRGBColorSpace, this.map.minFilter = d.LinearFilter, this.map.magFilter = d.LinearFilter, this.updateText(t, n, o, a);
39
+ this.qualityFactor = t, this.map = new l.CanvasTexture(this.canvas), this.map.colorSpace = l.LinearSRGBColorSpace, this.map.minFilter = l.LinearFilter, this.map.magFilter = l.LinearFilter, this.updateText(n, i, o, a);
40
40
  }
41
- wrapText(t, n) {
42
- const o = t.split(`
41
+ wrapText(n, i) {
42
+ const o = n.split(`
43
43
  `), a = [];
44
- return o.forEach((i) => {
45
- const c = i.match(/[^\s\-]+\-?|\-|\s+/g) || [];
46
- if (c.length === 0) {
44
+ return o.forEach((t) => {
45
+ const s = t.match(/[^\s\-]+\-?|\-|\s+/g) || [];
46
+ if (s.length === 0) {
47
47
  a.push("");
48
48
  return;
49
49
  }
50
- let s = c[0];
51
- for (let l = 1; l < c.length; l++) {
52
- const u = c[l];
53
- this.ctx.measureText(s + u).width <= n + 2 ? s += u : (s && a.push(s), s = u.trimStart());
50
+ let c = s[0];
51
+ for (let u = 1; u < s.length; u++) {
52
+ const d = s[u];
53
+ this.ctx.measureText(c + d).width <= i + 2 ? c += d : (c && a.push(c), c = d.trimStart());
54
54
  }
55
- s && a.push(s);
55
+ c && a.push(c);
56
56
  }), a;
57
57
  }
58
- updateText(t, n, o, a, i) {
59
- i !== void 0 && (this.qualityFactor = i);
60
- const s = (window.devicePixelRatio || 1) * this.qualityFactor, l = o * s, u = a * s;
61
- this.canvas.width !== l || this.canvas.height !== u ? (this.canvas.width = l, this.canvas.height = u) : this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height), this.ctx.setTransform(s, 0, 0, s, 0, 0), this.ctx.font = n.font, this.ctx.fillStyle = n.color, this.ctx.textBaseline = "alphabetic", this.ctx.globalAlpha = 1;
62
- const p = this.wrapText(t, o), m = n.lineHeight, v = A.getBaseline(n.font);
63
- p.forEach((f, y) => {
64
- const C = y * m + v;
65
- let b = 0;
66
- n.textAlign === "center" ? b = o / 2 : n.textAlign === "right" && (b = o), this.ctx.textAlign = n.textAlign, this.ctx.fillText(f, b, C);
58
+ updateText(n, i, o, a, t) {
59
+ t !== void 0 && (this.qualityFactor = t);
60
+ const c = (window.devicePixelRatio || 1) * this.qualityFactor, u = o * c, d = a * c;
61
+ this.canvas.width !== u || this.canvas.height !== d ? (this.canvas.width = u, this.canvas.height = d) : this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height), this.ctx.setTransform(c, 0, 0, c, 0, 0), this.ctx.font = i.font, this.ctx.fillStyle = i.color, this.ctx.textBaseline = "alphabetic", this.ctx.globalAlpha = 1;
62
+ const p = this.wrapText(n, o), g = i.lineHeight, f = I.getBaseline(i.font);
63
+ p.forEach((h, B) => {
64
+ const C = B * g + f;
65
+ let m = 0;
66
+ i.textAlign === "center" ? m = o / 2 : i.textAlign === "right" && (m = o), this.ctx.textAlign = i.textAlign, this.ctx.fillText(h, m, C);
67
67
  }), this.map && (this.map.needsUpdate = !0);
68
68
  }
69
69
  dispose() {
@@ -73,86 +73,102 @@ class G extends d.MeshBasicMaterial {
73
73
  function x(r, e = 0) {
74
74
  if (typeof r == "number")
75
75
  return r;
76
- const t = parseFloat(r) || 0;
77
- return typeof r == "string" && r.includes("%") ? t / 100 * e : t;
76
+ const n = parseFloat(r) || 0;
77
+ return typeof r == "string" && r.includes("%") ? n / 100 * e : n;
78
78
  }
79
79
  function w(r) {
80
80
  if (!r || r === "transparent")
81
- return { color: new d.Color(16777215), alpha: 0 };
81
+ return { color: new l.Color(16777215), alpha: 0 };
82
82
  const e = r.match(
83
83
  /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
84
84
  );
85
85
  if (e) {
86
- const t = parseInt(e[1], 10), n = parseInt(e[2], 10), o = parseInt(e[3], 10), a = e[4] !== void 0 ? parseFloat(e[4]) : 1;
87
- return { color: new d.Color(`rgb(${t}, ${n}, ${o})`), alpha: a };
86
+ const n = parseInt(e[1], 10), i = parseInt(e[2], 10), o = parseInt(e[3], 10), a = e[4] !== void 0 ? parseFloat(e[4]) : 1;
87
+ return { color: new l.Color(`rgb(${n}, ${i}, ${o})`), alpha: a };
88
88
  }
89
- return { color: new d.Color(r), alpha: 1 };
89
+ return { color: new l.Color(r), alpha: 1 };
90
90
  }
91
- function E(r) {
91
+ function A(r) {
92
92
  const e = [];
93
- let t = "", n = 0;
93
+ let n = "", i = 0;
94
94
  for (let o = 0; o < r.length; o++) {
95
95
  const a = r[o];
96
96
  if (a === "(")
97
- n++;
97
+ i++;
98
98
  else if (a === ")")
99
- n--;
100
- else if (a === "," && n === 0) {
101
- e.push(t.trim()), t = "";
99
+ i--;
100
+ else if (a === "," && i === 0) {
101
+ e.push(n.trim()), n = "";
102
102
  continue;
103
103
  }
104
- t += a;
104
+ n += a;
105
105
  }
106
- return t && e.push(t.trim()), e;
106
+ return n && e.push(n.trim()), e;
107
107
  }
108
- function L(r) {
108
+ function z(r) {
109
109
  if (!r || typeof r != "string" || !r.includes("linear-gradient"))
110
110
  return null;
111
111
  const e = r.match(/linear-gradient\((.*)\)/);
112
112
  if (!e)
113
113
  return null;
114
- const t = e[1], n = E(t);
114
+ const n = e[1], i = A(n);
115
115
  let o = Math.PI, a = 0;
116
- const i = n[0].trim();
117
- if (i.startsWith("to "))
118
- i === "to top" ? o = 0 : i === "to right" ? o = Math.PI / 2 : i === "to bottom" ? o = Math.PI : i === "to left" ? o = Math.PI * 1.5 : i === "to top right" || i === "to right top" ? o = Math.PI / 4 : i === "to bottom right" || i === "to right bottom" ? o = Math.PI * 0.75 : i === "to bottom left" || i === "to left bottom" ? o = Math.PI * 1.25 : (i === "to top left" || i === "to left top") && (o = Math.PI * 1.75), a = 1;
119
- else if (i.endsWith("deg") || i.endsWith("rad") || i.endsWith("turn")) {
120
- const s = parseFloat(i);
121
- i.endsWith("deg") ? o = s * (Math.PI / 180) : i.endsWith("rad") ? o = s : i.endsWith("turn") && (o = s * Math.PI * 2), a = 1;
116
+ const t = i[0].trim();
117
+ if (t.startsWith("to "))
118
+ t === "to top" ? o = 0 : t === "to right" ? o = Math.PI / 2 : t === "to bottom" ? o = Math.PI : t === "to left" ? o = Math.PI * 1.5 : t === "to top right" || t === "to right top" ? o = Math.PI / 4 : t === "to bottom right" || t === "to right bottom" ? o = Math.PI * 0.75 : t === "to bottom left" || t === "to left bottom" ? o = Math.PI * 1.25 : (t === "to top left" || t === "to left top") && (o = Math.PI * 1.75), a = 1;
119
+ else if (t.endsWith("deg") || t.endsWith("rad") || t.endsWith("turn")) {
120
+ const c = parseFloat(t);
121
+ t.endsWith("deg") ? o = c * (Math.PI / 180) : t.endsWith("rad") ? o = c : t.endsWith("turn") && (o = c * Math.PI * 2), a = 1;
122
122
  }
123
- const c = [];
124
- for (let s = a; s < n.length && !(c.length >= 8); s++) {
125
- const l = n[s].trim(), u = l.lastIndexOf(" ");
126
- let p = l, m = null;
127
- if (u !== -1 && !l.endsWith(")")) {
128
- const f = l.substring(u + 1);
129
- (f.endsWith("%") || f.endsWith("px") || !isNaN(parseFloat(f))) && (p = l.substring(0, u).trim(), m = f);
123
+ const s = [];
124
+ for (let c = a; c < i.length && !(s.length >= 8); c++) {
125
+ const u = i[c].trim(), d = u.lastIndexOf(" ");
126
+ let p = u, g = null;
127
+ if (d !== -1 && !u.endsWith(")")) {
128
+ const h = u.substring(d + 1);
129
+ (h.endsWith("%") || h.endsWith("px") || !isNaN(parseFloat(h))) && (p = u.substring(0, d).trim(), g = h);
130
130
  }
131
- const v = w(p);
132
- c.push({
133
- color: v.color,
134
- alpha: v.alpha,
135
- rawStop: m,
131
+ const f = w(p);
132
+ s.push({
133
+ color: f.color,
134
+ alpha: f.alpha,
135
+ rawStop: g,
136
136
  stop: 0
137
137
  });
138
138
  }
139
- if (c.length > 0) {
140
- for (let l = 0; l < c.length; l++)
141
- c[l].rawStop !== null && (c[l].stop = parseFloat(c[l].rawStop) / 100);
142
- c[0].rawStop === null && (c[0].stop = 0), c.length > 1 && c[c.length - 1].rawStop === null && (c[c.length - 1].stop = 1);
143
- let s = 0;
144
- for (let l = 1; l < c.length; l++)
145
- if (c[l].rawStop !== null || l === c.length - 1) {
146
- const u = l - s;
147
- if (u > 1) {
148
- const p = c[s].stop, v = (c[l].stop - p) / u;
149
- for (let f = 1; f < u; f++)
150
- c[s + f].stop = p + v * f;
139
+ if (s.length > 0) {
140
+ for (let u = 0; u < s.length; u++)
141
+ s[u].rawStop !== null && (s[u].stop = parseFloat(s[u].rawStop) / 100);
142
+ s[0].rawStop === null && (s[0].stop = 0), s.length > 1 && s[s.length - 1].rawStop === null && (s[s.length - 1].stop = 1);
143
+ let c = 0;
144
+ for (let u = 1; u < s.length; u++)
145
+ if (s[u].rawStop !== null || u === s.length - 1) {
146
+ const d = u - c;
147
+ if (d > 1) {
148
+ const p = s[c].stop, f = (s[u].stop - p) / d;
149
+ for (let h = 1; h < d; h++)
150
+ s[c + h].stop = p + f * h;
151
151
  }
152
- s = l;
152
+ c = u;
153
153
  }
154
154
  }
155
- return { angle: o, stops: c };
155
+ return { angle: o, stops: s };
156
+ }
157
+ function E(r) {
158
+ if (!r || r === "none")
159
+ return null;
160
+ const e = A(r);
161
+ if (e.length === 0)
162
+ return null;
163
+ const n = e[0].trim(), i = /(rgba?\([^)]+\)|#[0-9a-fA-F]+|[a-zA-Z]+)/, o = n.match(i);
164
+ let a = "transparent", t = n;
165
+ if (o && o[1] !== "inset") {
166
+ const f = o[1];
167
+ (f.startsWith("rgb") || f.startsWith("#") || f === "black" || f === "white" || f === "transparent") && (a = f, t = n.replace(f, "").trim());
168
+ }
169
+ t = t.replace("inset", "").trim();
170
+ const s = t.split(/\s+/).map((f) => parseFloat(f) || 0), c = s[0] || 0, u = s[1] || 0, d = s[2] || 0, p = s[3] || 0, g = w(a);
171
+ return { offsetX: c, offsetY: u, blurRadius: d, spreadRadius: p, color: g.color, alpha: g.alpha };
156
172
  }
157
173
  const F = `varying vec2 vUv;
158
174
  varying vec4 vScreenPos;
@@ -160,8 +176,13 @@ void main() {
160
176
  vUv = uv;
161
177
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
162
178
  vScreenPos = gl_Position;
163
- }`, U = `varying vec2 vUv;
179
+ }`, W = `varying vec2 vUv;
164
180
  uniform vec2 uSize;
181
+ uniform vec2 uMeshSize;
182
+ uniform vec4 uShadowColor;
183
+ uniform vec2 uShadowOffset;
184
+ uniform float uShadowBlur;
185
+ uniform float uShadowSpread;
165
186
  uniform vec4 uBorderRadius;
166
187
  uniform float uBorderWidth;
167
188
  uniform vec4 uBgColor;
@@ -217,11 +238,10 @@ float sdVisualBox(vec2 p, vec2 b, float r) {
217
238
  return min(max(q.x, q.y), 0.0) + d;
218
239
  }
219
240
 
220
- vec4 calculateGradientLayer(vec2 uv) {
241
+ vec4 calculateGradientLayer(vec2 p) {
221
242
  if (uGradientCount < 2) return uGradientColors[0];
222
243
 
223
244
  vec2 dir = vec2(sin(uGradientAngle), cos(uGradientAngle));
224
- vec2 p = (uv - 0.5) * uSize;
225
245
  float proj = dot(p, dir);
226
246
  float L = abs(uSize.x * dir.x) + abs(uSize.y * dir.y);
227
247
  float t = clamp((proj / L) + 0.5, 0.0, 1.0);
@@ -240,7 +260,7 @@ vec4 calculateGradientLayer(vec2 uv) {
240
260
  }
241
261
 
242
262
  void main() {
243
- vec2 p = (vUv - 0.5) * uSize;
263
+ vec2 p = (vUv - 0.5) * uMeshSize;
244
264
  vec2 halfSize = uSize * 0.5;
245
265
 
246
266
  // CSS Proportional Border-Radius Clamping
@@ -261,7 +281,7 @@ void main() {
261
281
  vec4 baseColor = vec4(uBgColor.rgb, uBgColor.a);
262
282
 
263
283
  if (uGradientCount > 0) {
264
- vec4 gradColor = calculateGradientLayer(vUv);
284
+ vec4 gradColor = calculateGradientLayer(p);
265
285
  baseColor = blendSrcOver(gradColor, baseColor);
266
286
  }
267
287
 
@@ -288,11 +308,26 @@ void main() {
288
308
 
289
309
  // final blending (border + background) using blendSrcOver
290
310
  vec4 borderLayer = vec4(uBorderColor.rgb, borderAlpha);
291
- vec4 finalColor = blendSrcOver(borderLayer, baseColor);
311
+ vec4 mainColor = blendSrcOver(borderLayer, baseColor);
312
+ mainColor.a *= bgMask;
313
+
314
+ vec4 shadowLayer = vec4(0.0);
315
+ if (uShadowColor.a > 0.001) {
316
+ vec2 shadowP = p - vec2(uShadowOffset.x, -uShadowOffset.y);
317
+ float shadowRadius = max(r + uShadowSpread, 0.0);
318
+ vec2 shadowHalfSize = max(halfSize + vec2(uShadowSpread), vec2(0.0));
319
+ float shadowD = sdRoundedBox(shadowP, shadowHalfSize, shadowRadius);
320
+ float blur = max(uShadowBlur, 1.0);
321
+ float shadowMask = 1.0 - smoothstep(-blur * 0.5, blur * 0.5, shadowD);
322
+ shadowLayer = vec4(uShadowColor.rgb, uShadowColor.a * shadowMask);
323
+ }
324
+
325
+ vec4 finalColor = blendSrcOver(mainColor, shadowLayer);
326
+
292
327
  // final color control (Tint, Noise)
293
328
  #INJECT_COLOR_MODIFIER
294
329
 
295
- float finalOpacity = finalColor.a * bgMask * uOpacity;
330
+ float finalOpacity = finalColor.a * uOpacity;
296
331
  if (finalOpacity < 0.001) discard;
297
332
 
298
333
  gl_FragColor = vec4(finalColor.rgb, finalOpacity);
@@ -300,41 +335,47 @@ void main() {
300
335
 
301
336
  // #include <colorspace_fragment>
302
337
  }
303
- `, W = `uniform sampler2D uTexture;
338
+ `, U = `uniform sampler2D uTexture;
304
339
  varying vec4 vScreenPos;
305
340
  uniform vec2 uTextureRepeat;
306
341
  uniform vec2 uTextureOffset;`, _ = `vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
307
342
  vec2 resultUv = screenUv;
308
343
 
309
- `, z = `vec4 texColor = texture2D(uTexture, resultUv);
344
+ `, V = `vec4 texColor = texture2D(uTexture, resultUv);
310
345
  baseColor = blendSrcOver(baseColor, texColor);`, R = {
311
346
  vertexShader: F,
312
- fragmentShader: U
313
- }, I = {
314
- declChunk: W,
347
+ fragmentShader: W
348
+ }, M = {
349
+ declChunk: U,
315
350
  uvChunk: _,
316
- baseColorChunk: z
351
+ baseColorChunk: V
317
352
  };
318
- function V(r, e, t, n = null, o) {
319
- const a = n !== null || !!r.imageSrc;
320
- let i = "";
321
- const c = {};
353
+ function D(r, e, n, i = null, o) {
354
+ const a = i !== null || !!r.imageSrc;
355
+ let t = "";
356
+ const s = {};
322
357
  if (o != null && o.uniforms)
323
- for (const [h, g] of Object.entries(o.uniforms))
324
- typeof g == "number" ? (i += `uniform float ${h};
325
- `, c[h] = { value: g }) : Array.isArray(g) ? g.length === 2 ? (i += `uniform vec2 ${h};
326
- `, c[h] = { value: new d.Vector2(...g) }) : g.length === 3 ? (i += `uniform vec3 ${h};
327
- `, c[h] = { value: new d.Vector3(...g) }) : g.length === 4 && (i += `uniform vec4 ${h};
328
- `, c[h] = { value: new d.Vector4(...g) }) : (i += `uniform float ${h};
329
- `, c[h] = { value: g });
330
- const s = o !== void 0, l = (a || s ? I.declChunk : "") + `
331
- ` + i, u = `vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
358
+ for (const [v, b] of Object.entries(o.uniforms))
359
+ typeof b == "number" ? (t += `uniform float ${v};
360
+ `, s[v] = { value: b }) : Array.isArray(b) ? b.length === 2 ? (t += `uniform vec2 ${v};
361
+ `, s[v] = { value: new l.Vector2(...b) }) : b.length === 3 ? (t += `uniform vec3 ${v};
362
+ `, s[v] = { value: new l.Vector3(...b) }) : b.length === 4 && (t += `uniform vec4 ${v};
363
+ `, s[v] = { value: new l.Vector4(...b) }) : (t += `uniform float ${v};
364
+ `, s[v] = { value: b });
365
+ const c = o !== void 0, u = (a || c ? M.declChunk : "") + `
366
+ ` + t, d = `vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
332
367
  `, p = r.isTraveler ? `vec2 resultUv = screenUv;
333
- ` : `vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
334
- `, m = a || s ? u + p + ((o == null ? void 0 : o.uvModifier) || "") : "", v = a || s ? I.baseColorChunk : "", f = (o == null ? void 0 : o.colorModifier) || "", y = R.fragmentShader.replace("#INJECT_DECLARATIONS", l).replace("#INJECT_UV_MODIFIER", m).replace("#INJECT_BASE_COLOR", v).replace("#INJECT_COLOR_MODIFIER", f), C = w(r.backgroundColor), b = w(r.borderColor), B = {
335
- uSize: { value: new d.Vector2(e, t) },
368
+ ` : `vec2 localUv = (p / uSize) + 0.5;
369
+ vec2 resultUv = localUv * uTextureRepeat + uTextureOffset;
370
+ `, g = a || c ? d + p + ((o == null ? void 0 : o.uvModifier) || "") : "", f = a || c ? M.baseColorChunk : "", h = (o == null ? void 0 : o.colorModifier) || "", B = R.fragmentShader.replace("#INJECT_DECLARATIONS", u).replace("#INJECT_UV_MODIFIER", g).replace("#INJECT_BASE_COLOR", f).replace("#INJECT_COLOR_MODIFIER", h), C = w(r.backgroundColor), m = w(r.borderColor), y = {
371
+ uSize: { value: new l.Vector2(e, n) },
372
+ uMeshSize: { value: new l.Vector2(e, n) },
373
+ uShadowColor: { value: new l.Vector4(0, 0, 0, 0) },
374
+ uShadowOffset: { value: new l.Vector2(0, 0) },
375
+ uShadowBlur: { value: 0 },
376
+ uShadowSpread: { value: 0 },
336
377
  uBgColor: {
337
- value: new d.Vector4(
378
+ value: new l.Vector4(
338
379
  C.color.r,
339
380
  C.color.g,
340
381
  C.color.b,
@@ -342,56 +383,65 @@ function V(r, e, t, n = null, o) {
342
383
  )
343
384
  },
344
385
  uBorderColor: {
345
- value: new d.Vector4(
346
- b.color.r,
347
- b.color.g,
348
- b.color.b,
349
- b.alpha
386
+ value: new l.Vector4(
387
+ m.color.r,
388
+ m.color.g,
389
+ m.color.b,
390
+ m.alpha
350
391
  )
351
392
  },
352
- uBorderRadius: { value: new d.Vector4(0, 0, 0, 0) },
393
+ uBorderRadius: { value: new l.Vector4(0, 0, 0, 0) },
353
394
  uBorderWidth: { value: x(r.borderWidth) },
354
395
  uOpacity: { value: r.opacity ?? 1 },
355
396
  uTexture: { value: null },
356
- uTextureRepeat: { value: new d.Vector2(1, 1) },
357
- uTextureOffset: { value: new d.Vector2(0, 0) },
397
+ uTextureRepeat: { value: new l.Vector2(1, 1) },
398
+ uTextureOffset: { value: new l.Vector2(0, 0) },
358
399
  uGradientCount: { value: 0 },
359
400
  uGradientAngle: { value: 0 },
360
401
  uGradientColors: {
361
- value: Array.from({ length: 8 }, () => new d.Vector4(0, 0, 0, 0))
402
+ value: Array.from({ length: 8 }, () => new l.Vector4(0, 0, 0, 0))
362
403
  },
363
404
  uGradientStops: { value: new Float32Array(8) }
364
405
  };
365
- M(B.uBorderRadius.value, r.borderRadius, Math.min(e, t)), a && (B.uTexture.value = n);
366
- const O = new d.ShaderMaterial({
367
- uniforms: { ...B, ...c },
406
+ P(y.uBorderRadius.value, r.borderRadius, Math.min(e, n)), a && (y.uTexture.value = i);
407
+ const O = new l.ShaderMaterial({
408
+ uniforms: { ...y, ...s },
368
409
  vertexShader: R.vertexShader,
369
- fragmentShader: y,
410
+ fragmentShader: B,
370
411
  transparent: !0,
371
- side: d.FrontSide
412
+ side: l.FrontSide
372
413
  // for better performance
373
414
  });
374
415
  return r.backgroundImage && T(O, { backgroundImage: r.backgroundImage }), O;
375
416
  }
376
- function D(r, e, t, n, o) {
417
+ function H(r, e, n, i, o) {
377
418
  const a = x(e.borderWidth);
378
419
  T(r, {
379
- width: t,
380
- height: n,
420
+ width: n,
421
+ height: i,
381
422
  borderRadius: e.borderRadius,
382
423
  borderWidth: a,
383
424
  backgroundColor: e.backgroundColor,
384
425
  borderColor: e.borderColor,
385
426
  opacity: e.opacity,
386
427
  texture: o,
387
- backgroundImage: e.backgroundImage
428
+ backgroundImage: e.backgroundImage,
429
+ boxShadow: e.boxShadow
388
430
  });
389
431
  }
390
432
  function T(r, e) {
391
- var n;
392
- if (e.width !== void 0 && e.height !== void 0 && r.uniforms.uSize.value.set(e.width, e.height), e.borderRadius !== void 0) {
433
+ var i;
434
+ if (e.boxShadow !== void 0) {
435
+ const o = E(e.boxShadow);
436
+ o ? (r.uniforms.uShadowColor.value.set(o.color.r, o.color.g, o.color.b, o.alpha), r.uniforms.uShadowOffset.value.set(o.offsetX, o.offsetY), r.uniforms.uShadowBlur.value = o.blurRadius, r.uniforms.uShadowSpread.value = o.spreadRadius, r.userData.shadowPadding = o.blurRadius + o.spreadRadius + Math.max(Math.abs(o.offsetX), Math.abs(o.offsetY))) : (r.uniforms.uShadowColor.value.w = 0, r.userData.shadowPadding = 0);
437
+ }
438
+ if (e.width !== void 0 && e.height !== void 0 && r.uniforms.uSize.value.set(e.width, e.height), r.uniforms.uMeshSize) {
439
+ const o = r.userData.shadowPadding || 0, a = e.width !== void 0 ? e.width : r.uniforms.uSize.value.x, t = e.height !== void 0 ? e.height : r.uniforms.uSize.value.y;
440
+ r.uniforms.uMeshSize.value.set(a + o * 2, t + o * 2);
441
+ }
442
+ if (e.borderRadius !== void 0) {
393
443
  const o = e.width !== void 0 && e.height !== void 0 ? Math.min(e.width, e.height) : Math.min(r.uniforms.uSize.value.x, r.uniforms.uSize.value.y);
394
- M(r.uniforms.uBorderRadius.value, e.borderRadius, o);
444
+ P(r.uniforms.uBorderRadius.value, e.borderRadius, o);
395
445
  }
396
446
  if (e.borderWidth !== void 0 && (r.uniforms.uBorderWidth.value = e.borderWidth), e.backgroundColor !== void 0)
397
447
  if (Array.isArray(e.backgroundColor)) {
@@ -446,43 +496,43 @@ function T(r, e) {
446
496
  );
447
497
  }
448
498
  e.opacity !== void 0 && (r.uniforms.uOpacity.value = e.opacity), e.bgOpacity !== void 0 && (r.uniforms.uBgColor.value.w = e.bgOpacity), e.borderOpacity !== void 0 && (r.uniforms.uBorderColor.value.w = e.borderOpacity), r.uniforms.uTexture && e.texture !== void 0 && (r.uniforms.uTexture.value = e.texture);
449
- const t = e.texture !== void 0 ? e.texture : (n = r.uniforms.uTexture) == null ? void 0 : n.value;
450
- if (t && (t.image instanceof ImageBitmap || t.image instanceof HTMLImageElement || t.image instanceof HTMLCanvasElement)) {
451
- const o = t.image.naturalWidth || t.image.videoWidth || t.image.width, a = t.image.naturalHeight || t.image.videoHeight || t.image.height, i = e.width ?? r.uniforms.uSize.value.x, c = e.height ?? r.uniforms.uSize.value.y;
452
- if (o && a && i && c) {
453
- const s = o / a, l = i / c;
454
- if (s > l) {
455
- const u = l / s;
456
- r.uniforms.uTextureRepeat.value.set(u, 1), r.uniforms.uTextureOffset.value.set((1 - u) / 2, 0);
499
+ const n = e.texture !== void 0 ? e.texture : (i = r.uniforms.uTexture) == null ? void 0 : i.value;
500
+ if (n && (n.image instanceof ImageBitmap || n.image instanceof HTMLImageElement || n.image instanceof HTMLCanvasElement)) {
501
+ const o = n.image.naturalWidth || n.image.videoWidth || n.image.width, a = n.image.naturalHeight || n.image.videoHeight || n.image.height, t = e.width ?? r.uniforms.uSize.value.x, s = e.height ?? r.uniforms.uSize.value.y;
502
+ if (o && a && t && s) {
503
+ const c = o / a, u = t / s;
504
+ if (c > u) {
505
+ const d = u / c;
506
+ r.uniforms.uTextureRepeat.value.set(d, 1), r.uniforms.uTextureOffset.value.set((1 - d) / 2, 0);
457
507
  } else {
458
- const u = s / l;
459
- r.uniforms.uTextureRepeat.value.set(1, u), r.uniforms.uTextureOffset.value.set(0, (1 - u) / 2);
508
+ const d = c / u;
509
+ r.uniforms.uTextureRepeat.value.set(1, d), r.uniforms.uTextureOffset.value.set(0, (1 - d) / 2);
460
510
  }
461
511
  }
462
512
  } else
463
513
  r.uniforms.uTextureRepeat && (r.uniforms.uTextureRepeat.value.set(1, 1), r.uniforms.uTextureOffset.value.set(0, 0));
464
514
  if (e.backgroundImage !== void 0) {
465
- const o = L(e.backgroundImage);
515
+ const o = z(e.backgroundImage);
466
516
  if (o) {
467
517
  r.uniforms.uGradientCount.value = o.stops.length, r.uniforms.uGradientAngle.value = o.angle;
468
518
  for (let a = 0; a < 8; a++)
469
519
  if (a < o.stops.length) {
470
- const i = o.stops[a];
520
+ const t = o.stops[a];
471
521
  r.uniforms.uGradientColors.value[a].set(
472
- i.color.r,
473
- i.color.g,
474
- i.color.b,
475
- i.alpha
476
- ), r.uniforms.uGradientStops.value[a] = i.stop;
522
+ t.color.r,
523
+ t.color.g,
524
+ t.color.b,
525
+ t.alpha
526
+ ), r.uniforms.uGradientStops.value[a] = t.stop;
477
527
  } else
478
528
  r.uniforms.uGradientColors.value[a].set(0, 0, 0, 0), r.uniforms.uGradientStops.value[a] = 1;
479
529
  } else
480
530
  r.uniforms.uGradientCount.value = 0;
481
531
  }
482
532
  for (const o of Object.keys(e))
483
- o !== "width" && o !== "height" && o !== "borderRadius" && o !== "borderWidth" && o !== "backgroundColor" && o !== "borderColor" && o !== "opacity" && o !== "bgOpacity" && o !== "borderOpacity" && o !== "texture" && o !== "backgroundImage" && r.uniforms[o] !== void 0 && (r.uniforms[o].value !== void 0 && r.uniforms[o].value !== null && typeof r.uniforms[o].value.set == "function" ? Array.isArray(e[o]) ? r.uniforms[o].value.set(...e[o]) : e[o] !== void 0 && (e[o].copy ? r.uniforms[o].value.copy(e[o]) : r.uniforms[o].value = e[o]) : r.uniforms[o].value = e[o]);
533
+ o !== "width" && o !== "height" && o !== "borderRadius" && o !== "borderWidth" && o !== "backgroundColor" && o !== "borderColor" && o !== "opacity" && o !== "bgOpacity" && o !== "borderOpacity" && o !== "texture" && o !== "backgroundImage" && o !== "boxShadow" && r.uniforms[o] !== void 0 && (r.uniforms[o].value !== void 0 && r.uniforms[o].value !== null && typeof r.uniforms[o].value.set == "function" ? Array.isArray(e[o]) ? r.uniforms[o].value.set(...e[o]) : e[o] !== void 0 && (e[o].copy ? r.uniforms[o].value.copy(e[o]) : r.uniforms[o].value = e[o]) : r.uniforms[o].value = e[o]);
484
534
  }
485
- function M(r, e, t = 0) {
535
+ function P(r, e, n = 0) {
486
536
  if (e == null) {
487
537
  r.set(0, 0, 0, 0);
488
538
  return;
@@ -495,38 +545,38 @@ function M(r, e, t = 0) {
495
545
  r.set(e[0], e[1], e[2], e[3]);
496
546
  return;
497
547
  }
498
- const n = e.split("/")[0].trim().split(/\s+/), o = x(n[0], t), a = x(n[1] ?? n[0], t), i = x(n[2] ?? n[0], t), c = x(n[3] ?? n[1] ?? n[0], t);
499
- r.set(o, a, i, c);
548
+ const i = e.split("/")[0].trim().split(/\s+/), o = x(i[0], n), a = x(i[1] ?? i[0], n), t = x(i[2] ?? i[0], n), s = x(i[3] ?? i[1] ?? i[0], n);
549
+ r.set(o, a, t, s);
500
550
  }
501
- const N = {
502
- create(r, e, t, n, o, a = 2, i = null, c) {
503
- return r === "BOX" ? V(
551
+ const q = {
552
+ create(r, e, n, i, o, a = 2, t = null, s) {
553
+ return r === "BOX" ? D(
504
554
  e,
505
- n,
506
- o,
507
555
  i,
508
- c
509
- ) : r === "TEXT" ? new G(
510
- t || "",
556
+ o,
557
+ t,
558
+ s
559
+ ) : r === "TEXT" ? new L(
560
+ n || "",
511
561
  e,
512
- n,
562
+ i,
513
563
  o,
514
564
  a
515
- ) : new d.MeshBasicMaterial({ visible: !1 });
565
+ ) : new l.MeshBasicMaterial({ visible: !1 });
516
566
  },
517
- update(r, e, t, n, o, a, i = 2, c) {
518
- e === "BOX" ? D(
567
+ update(r, e, n, i, o, a, t = 2, s) {
568
+ e === "BOX" ? H(
519
569
  r,
520
- t,
570
+ n,
521
571
  o,
522
572
  a,
523
- c
573
+ s
524
574
  ) : e === "TEXT" && r.updateText(
525
- n || "",
526
- t,
575
+ i || "",
576
+ n,
527
577
  o,
528
578
  a,
529
- i
579
+ t
530
580
  );
531
581
  },
532
582
  forceUpdateUniforms(r, e) {
@@ -534,13 +584,14 @@ const N = {
534
584
  }
535
585
  };
536
586
  export {
537
- N as Painter,
538
- G as TextGenerator,
539
- V as createBoxMaterial,
587
+ q as Painter,
588
+ L as TextGenerator,
589
+ D as createBoxMaterial,
590
+ E as parseBoxShadow,
540
591
  w as parseColor,
541
- L as parseLinearGradient,
592
+ z as parseLinearGradient,
542
593
  x as parsePixelValue,
543
594
  T as setBoxUniforms,
544
- E as splitByComma,
545
- D as updateBoxMaterial
595
+ A as splitByComma,
596
+ H as updateBoxMaterial
546
597
  };
@@ -1,12 +1,17 @@
1
- (function(f,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],p):(f=typeof globalThis<"u"?globalThis:f||self,p(f.MiragePainter={},f.THREE))})(this,function(f,p){"use strict";var q=Object.defineProperty;var j=(f,p,x)=>p in f?q(f,p,{enumerable:!0,configurable:!0,writable:!0,value:x}):f[p]=x;var T=(f,p,x)=>(j(f,typeof p!="symbol"?p+"":p,x),x);function x(r){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const t in r)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>r[t]})}}return e.default=r,Object.freeze(e)}const u=x(p);class M{static getBaseline(e){if(this.cache.has(e))return this.cache.get(e);const t=this.calculateBaselineFromDOM(e);return this.cache.set(e,t),t}static calculateBaselineFromDOM(e){if(typeof document>"u")return 0;const t=document.createElement("div"),n=document.createElement("span"),o=document.createElement("img");t.style.visibility="hidden",t.style.position="absolute",t.style.top="0px",t.style.left="0px",t.style.font=e,t.style.margin="0",t.style.padding="0",t.style.border="none",n.style.margin="0",n.style.padding="0",n.style.border="none",n.style.lineHeight="normal",o.width=1,o.height=1,o.style.verticalAlign="baseline",n.appendChild(document.createTextNode("Hidden Text")),t.appendChild(n),t.appendChild(o),document.body.appendChild(t);const a=o.offsetTop-n.offsetTop;return document.body.removeChild(t),a}}T(M,"cache",new Map);class A extends u.MeshBasicMaterial{constructor(t,n,o,a,i=2){super({transparent:!0,side:u.FrontSide,color:16777215});T(this,"canvas");T(this,"ctx");T(this,"qualityFactor");if(this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),!this.ctx)throw new Error("[Mirage] Failed to create canvas context");this.qualityFactor=i,this.map=new u.CanvasTexture(this.canvas),this.map.colorSpace=u.LinearSRGBColorSpace,this.map.minFilter=u.LinearFilter,this.map.magFilter=u.LinearFilter,this.updateText(t,n,o,a)}wrapText(t,n){const o=t.split(`
2
- `),a=[];return o.forEach(i=>{const c=i.match(/[^\s\-]+\-?|\-|\s+/g)||[];if(c.length===0){a.push("");return}let s=c[0];for(let l=1;l<c.length;l++){const d=c[l];this.ctx.measureText(s+d).width<=n+2?s+=d:(s&&a.push(s),s=d.trimStart())}s&&a.push(s)}),a}updateText(t,n,o,a,i){i!==void 0&&(this.qualityFactor=i);const s=(window.devicePixelRatio||1)*this.qualityFactor,l=o*s,d=a*s;this.canvas.width!==l||this.canvas.height!==d?(this.canvas.width=l,this.canvas.height=d):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.setTransform(s,0,0,s,0,0),this.ctx.font=n.font,this.ctx.fillStyle=n.color,this.ctx.textBaseline="alphabetic",this.ctx.globalAlpha=1;const g=this.wrapText(t,o),y=n.lineHeight,m=M.getBaseline(n.font);g.forEach((h,R)=>{const w=R*y+m;let C=0;n.textAlign==="center"?C=o/2:n.textAlign==="right"&&(C=o),this.ctx.textAlign=n.textAlign,this.ctx.fillText(h,C,w)}),this.map&&(this.map.needsUpdate=!0)}dispose(){this.map&&this.map.dispose(),super.dispose()}}function S(r,e=0){if(typeof r=="number")return r;const t=parseFloat(r)||0;return typeof r=="string"&&r.includes("%")?t/100*e:t}function B(r){if(!r||r==="transparent")return{color:new u.Color(16777215),alpha:0};const e=r.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(e){const t=parseInt(e[1],10),n=parseInt(e[2],10),o=parseInt(e[3],10),a=e[4]!==void 0?parseFloat(e[4]):1;return{color:new u.Color(`rgb(${t}, ${n}, ${o})`),alpha:a}}return{color:new u.Color(r),alpha:1}}function P(r){const e=[];let t="",n=0;for(let o=0;o<r.length;o++){const a=r[o];if(a==="(")n++;else if(a===")")n--;else if(a===","&&n===0){e.push(t.trim()),t="";continue}t+=a}return t&&e.push(t.trim()),e}function k(r){if(!r||typeof r!="string"||!r.includes("linear-gradient"))return null;const e=r.match(/linear-gradient\((.*)\)/);if(!e)return null;const t=e[1],n=P(t);let o=Math.PI,a=0;const i=n[0].trim();if(i.startsWith("to "))i==="to top"?o=0:i==="to right"?o=Math.PI/2:i==="to bottom"?o=Math.PI:i==="to left"?o=Math.PI*1.5:i==="to top right"||i==="to right top"?o=Math.PI/4:i==="to bottom right"||i==="to right bottom"?o=Math.PI*.75:i==="to bottom left"||i==="to left bottom"?o=Math.PI*1.25:(i==="to top left"||i==="to left top")&&(o=Math.PI*1.75),a=1;else if(i.endsWith("deg")||i.endsWith("rad")||i.endsWith("turn")){const s=parseFloat(i);i.endsWith("deg")?o=s*(Math.PI/180):i.endsWith("rad")?o=s:i.endsWith("turn")&&(o=s*Math.PI*2),a=1}const c=[];for(let s=a;s<n.length&&!(c.length>=8);s++){const l=n[s].trim(),d=l.lastIndexOf(" ");let g=l,y=null;if(d!==-1&&!l.endsWith(")")){const h=l.substring(d+1);(h.endsWith("%")||h.endsWith("px")||!isNaN(parseFloat(h)))&&(g=l.substring(0,d).trim(),y=h)}const m=B(g);c.push({color:m.color,alpha:m.alpha,rawStop:y,stop:0})}if(c.length>0){for(let l=0;l<c.length;l++)c[l].rawStop!==null&&(c[l].stop=parseFloat(c[l].rawStop)/100);c[0].rawStop===null&&(c[0].stop=0),c.length>1&&c[c.length-1].rawStop===null&&(c[c.length-1].stop=1);let s=0;for(let l=1;l<c.length;l++)if(c[l].rawStop!==null||l===c.length-1){const d=l-s;if(d>1){const g=c[s].stop,m=(c[l].stop-g)/d;for(let h=1;h<d;h++)c[s+h].stop=g+m*h}s=l}}return{angle:o,stops:c}}const W=`varying vec2 vUv;
1
+ (function(f,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],p):(f=typeof globalThis<"u"?globalThis:f||self,p(f.MiragePainter={},f.THREE))})(this,function(f,p){"use strict";var j=Object.defineProperty;var J=(f,p,w)=>p in f?j(f,p,{enumerable:!0,configurable:!0,writable:!0,value:w}):f[p]=w;var T=(f,p,w)=>(J(f,typeof p!="symbol"?p+"":p,w),w);function w(r){const o=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const n in r)if(n!=="default"){const t=Object.getOwnPropertyDescriptor(r,n);Object.defineProperty(o,n,t.get?t:{enumerable:!0,get:()=>r[n]})}}return o.default=r,Object.freeze(o)}const l=w(p);class P{static getBaseline(o){if(this.cache.has(o))return this.cache.get(o);const n=this.calculateBaselineFromDOM(o);return this.cache.set(o,n),n}static calculateBaselineFromDOM(o){if(typeof document>"u")return 0;const n=document.createElement("div"),t=document.createElement("span"),e=document.createElement("img");n.style.visibility="hidden",n.style.position="absolute",n.style.top="0px",n.style.left="0px",n.style.font=o,n.style.margin="0",n.style.padding="0",n.style.border="none",t.style.margin="0",t.style.padding="0",t.style.border="none",t.style.lineHeight="normal",e.width=1,e.height=1,e.style.verticalAlign="baseline",t.appendChild(document.createTextNode("Hidden Text")),n.appendChild(t),n.appendChild(e),document.body.appendChild(n);const a=e.offsetTop-t.offsetTop;return document.body.removeChild(n),a}}T(P,"cache",new Map);class A extends l.MeshBasicMaterial{constructor(n,t,e,a,i=2){super({transparent:!0,side:l.FrontSide,color:16777215});T(this,"canvas");T(this,"ctx");T(this,"qualityFactor");if(this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),!this.ctx)throw new Error("[Mirage] Failed to create canvas context");this.qualityFactor=i,this.map=new l.CanvasTexture(this.canvas),this.map.colorSpace=l.LinearSRGBColorSpace,this.map.minFilter=l.LinearFilter,this.map.magFilter=l.LinearFilter,this.updateText(n,t,e,a)}wrapText(n,t){const e=n.split(`
2
+ `),a=[];return e.forEach(i=>{const s=i.match(/[^\s\-]+\-?|\-|\s+/g)||[];if(s.length===0){a.push("");return}let c=s[0];for(let u=1;u<s.length;u++){const d=s[u];this.ctx.measureText(c+d).width<=t+2?c+=d:(c&&a.push(c),c=d.trimStart())}c&&a.push(c)}),a}updateText(n,t,e,a,i){i!==void 0&&(this.qualityFactor=i);const c=(window.devicePixelRatio||1)*this.qualityFactor,u=e*c,d=a*c;this.canvas.width!==u||this.canvas.height!==d?(this.canvas.width=u,this.canvas.height=d):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.setTransform(c,0,0,c,0,0),this.ctx.font=t.font,this.ctx.fillStyle=t.color,this.ctx.textBaseline="alphabetic",this.ctx.globalAlpha=1;const b=this.wrapText(n,e),v=t.lineHeight,h=P.getBaseline(t.font);b.forEach((g,M)=>{const B=M*v+h;let x=0;t.textAlign==="center"?x=e/2:t.textAlign==="right"&&(x=e),this.ctx.textAlign=t.textAlign,this.ctx.fillText(g,x,B)}),this.map&&(this.map.needsUpdate=!0)}dispose(){this.map&&this.map.dispose(),super.dispose()}}function S(r,o=0){if(typeof r=="number")return r;const n=parseFloat(r)||0;return typeof r=="string"&&r.includes("%")?n/100*o:n}function y(r){if(!r||r==="transparent")return{color:new l.Color(16777215),alpha:0};const o=r.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(o){const n=parseInt(o[1],10),t=parseInt(o[2],10),e=parseInt(o[3],10),a=o[4]!==void 0?parseFloat(o[4]):1;return{color:new l.Color(`rgb(${n}, ${t}, ${e})`),alpha:a}}return{color:new l.Color(r),alpha:1}}function R(r){const o=[];let n="",t=0;for(let e=0;e<r.length;e++){const a=r[e];if(a==="(")t++;else if(a===")")t--;else if(a===","&&t===0){o.push(n.trim()),n="";continue}n+=a}return n&&o.push(n.trim()),o}function k(r){if(!r||typeof r!="string"||!r.includes("linear-gradient"))return null;const o=r.match(/linear-gradient\((.*)\)/);if(!o)return null;const n=o[1],t=R(n);let e=Math.PI,a=0;const i=t[0].trim();if(i.startsWith("to "))i==="to top"?e=0:i==="to right"?e=Math.PI/2:i==="to bottom"?e=Math.PI:i==="to left"?e=Math.PI*1.5:i==="to top right"||i==="to right top"?e=Math.PI/4:i==="to bottom right"||i==="to right bottom"?e=Math.PI*.75:i==="to bottom left"||i==="to left bottom"?e=Math.PI*1.25:(i==="to top left"||i==="to left top")&&(e=Math.PI*1.75),a=1;else if(i.endsWith("deg")||i.endsWith("rad")||i.endsWith("turn")){const c=parseFloat(i);i.endsWith("deg")?e=c*(Math.PI/180):i.endsWith("rad")?e=c:i.endsWith("turn")&&(e=c*Math.PI*2),a=1}const s=[];for(let c=a;c<t.length&&!(s.length>=8);c++){const u=t[c].trim(),d=u.lastIndexOf(" ");let b=u,v=null;if(d!==-1&&!u.endsWith(")")){const g=u.substring(d+1);(g.endsWith("%")||g.endsWith("px")||!isNaN(parseFloat(g)))&&(b=u.substring(0,d).trim(),v=g)}const h=y(b);s.push({color:h.color,alpha:h.alpha,rawStop:v,stop:0})}if(s.length>0){for(let u=0;u<s.length;u++)s[u].rawStop!==null&&(s[u].stop=parseFloat(s[u].rawStop)/100);s[0].rawStop===null&&(s[0].stop=0),s.length>1&&s[s.length-1].rawStop===null&&(s[s.length-1].stop=1);let c=0;for(let u=1;u<s.length;u++)if(s[u].rawStop!==null||u===s.length-1){const d=u-c;if(d>1){const b=s[c].stop,h=(s[u].stop-b)/d;for(let g=1;g<d;g++)s[c+g].stop=b+h*g}c=u}}return{angle:e,stops:s}}function G(r){if(!r||r==="none")return null;const o=R(r);if(o.length===0)return null;const n=o[0].trim(),t=/(rgba?\([^)]+\)|#[0-9a-fA-F]+|[a-zA-Z]+)/,e=n.match(t);let a="transparent",i=n;if(e&&e[1]!=="inset"){const h=e[1];(h.startsWith("rgb")||h.startsWith("#")||h==="black"||h==="white"||h==="transparent")&&(a=h,i=n.replace(h,"").trim())}i=i.replace("inset","").trim();const s=i.split(/\s+/).map(h=>parseFloat(h)||0),c=s[0]||0,u=s[1]||0,d=s[2]||0,b=s[3]||0,v=y(a);return{offsetX:c,offsetY:u,blurRadius:d,spreadRadius:b,color:v.color,alpha:v.alpha}}const U=`varying vec2 vUv;
3
3
  varying vec4 vScreenPos;
4
4
  void main() {
5
5
  vUv = uv;
6
6
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
7
7
  vScreenPos = gl_Position;
8
- }`,z=`varying vec2 vUv;
8
+ }`,V=`varying vec2 vUv;
9
9
  uniform vec2 uSize;
10
+ uniform vec2 uMeshSize;
11
+ uniform vec4 uShadowColor;
12
+ uniform vec2 uShadowOffset;
13
+ uniform float uShadowBlur;
14
+ uniform float uShadowSpread;
10
15
  uniform vec4 uBorderRadius;
11
16
  uniform float uBorderWidth;
12
17
  uniform vec4 uBgColor;
@@ -62,11 +67,10 @@ float sdVisualBox(vec2 p, vec2 b, float r) {
62
67
  return min(max(q.x, q.y), 0.0) + d;
63
68
  }
64
69
 
65
- vec4 calculateGradientLayer(vec2 uv) {
70
+ vec4 calculateGradientLayer(vec2 p) {
66
71
  if (uGradientCount < 2) return uGradientColors[0];
67
72
 
68
73
  vec2 dir = vec2(sin(uGradientAngle), cos(uGradientAngle));
69
- vec2 p = (uv - 0.5) * uSize;
70
74
  float proj = dot(p, dir);
71
75
  float L = abs(uSize.x * dir.x) + abs(uSize.y * dir.y);
72
76
  float t = clamp((proj / L) + 0.5, 0.0, 1.0);
@@ -85,7 +89,7 @@ vec4 calculateGradientLayer(vec2 uv) {
85
89
  }
86
90
 
87
91
  void main() {
88
- vec2 p = (vUv - 0.5) * uSize;
92
+ vec2 p = (vUv - 0.5) * uMeshSize;
89
93
  vec2 halfSize = uSize * 0.5;
90
94
 
91
95
  // CSS Proportional Border-Radius Clamping
@@ -106,7 +110,7 @@ void main() {
106
110
  vec4 baseColor = vec4(uBgColor.rgb, uBgColor.a);
107
111
 
108
112
  if (uGradientCount > 0) {
109
- vec4 gradColor = calculateGradientLayer(vUv);
113
+ vec4 gradColor = calculateGradientLayer(p);
110
114
  baseColor = blendSrcOver(gradColor, baseColor);
111
115
  }
112
116
 
@@ -133,11 +137,26 @@ void main() {
133
137
 
134
138
  // final blending (border + background) using blendSrcOver
135
139
  vec4 borderLayer = vec4(uBorderColor.rgb, borderAlpha);
136
- vec4 finalColor = blendSrcOver(borderLayer, baseColor);
140
+ vec4 mainColor = blendSrcOver(borderLayer, baseColor);
141
+ mainColor.a *= bgMask;
142
+
143
+ vec4 shadowLayer = vec4(0.0);
144
+ if (uShadowColor.a > 0.001) {
145
+ vec2 shadowP = p - vec2(uShadowOffset.x, -uShadowOffset.y);
146
+ float shadowRadius = max(r + uShadowSpread, 0.0);
147
+ vec2 shadowHalfSize = max(halfSize + vec2(uShadowSpread), vec2(0.0));
148
+ float shadowD = sdRoundedBox(shadowP, shadowHalfSize, shadowRadius);
149
+ float blur = max(uShadowBlur, 1.0);
150
+ float shadowMask = 1.0 - smoothstep(-blur * 0.5, blur * 0.5, shadowD);
151
+ shadowLayer = vec4(uShadowColor.rgb, uShadowColor.a * shadowMask);
152
+ }
153
+
154
+ vec4 finalColor = blendSrcOver(mainColor, shadowLayer);
155
+
137
156
  // final color control (Tint, Noise)
138
157
  #INJECT_COLOR_MODIFIER
139
158
 
140
- float finalOpacity = finalColor.a * bgMask * uOpacity;
159
+ float finalOpacity = finalColor.a * uOpacity;
141
160
  if (finalOpacity < 0.001) discard;
142
161
 
143
162
  gl_FragColor = vec4(finalColor.rgb, finalOpacity);
@@ -145,20 +164,21 @@ void main() {
145
164
 
146
165
  // #include <colorspace_fragment>
147
166
  }
148
- `,V=`uniform sampler2D uTexture;
167
+ `,D=`uniform sampler2D uTexture;
149
168
  varying vec4 vScreenPos;
150
169
  uniform vec2 uTextureRepeat;
151
- uniform vec2 uTextureOffset;`,D=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
170
+ uniform vec2 uTextureOffset;`,H=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
152
171
  vec2 resultUv = screenUv;
153
172
 
154
- `,H=`vec4 texColor = texture2D(uTexture, resultUv);
155
- baseColor = blendSrcOver(baseColor, texColor);`,G={vertexShader:W,fragmentShader:z},E={declChunk:V,uvChunk:D,baseColorChunk:H};function L(r,e,t,n=null,o){const a=n!==null||!!r.imageSrc;let i="";const c={};if(o!=null&&o.uniforms)for(const[v,b]of Object.entries(o.uniforms))typeof b=="number"?(i+=`uniform float ${v};
156
- `,c[v]={value:b}):Array.isArray(b)?b.length===2?(i+=`uniform vec2 ${v};
157
- `,c[v]={value:new u.Vector2(...b)}):b.length===3?(i+=`uniform vec3 ${v};
158
- `,c[v]={value:new u.Vector3(...b)}):b.length===4&&(i+=`uniform vec4 ${v};
159
- `,c[v]={value:new u.Vector4(...b)}):(i+=`uniform float ${v};
160
- `,c[v]={value:b});const s=o!==void 0,l=(a||s?E.declChunk:"")+`
173
+ `,N=`vec4 texColor = texture2D(uTexture, resultUv);
174
+ baseColor = blendSrcOver(baseColor, texColor);`,E={vertexShader:U,fragmentShader:V},L={declChunk:D,uvChunk:H,baseColorChunk:N};function z(r,o,n,t=null,e){const a=t!==null||!!r.imageSrc;let i="";const s={};if(e!=null&&e.uniforms)for(const[m,C]of Object.entries(e.uniforms))typeof C=="number"?(i+=`uniform float ${m};
175
+ `,s[m]={value:C}):Array.isArray(C)?C.length===2?(i+=`uniform vec2 ${m};
176
+ `,s[m]={value:new l.Vector2(...C)}):C.length===3?(i+=`uniform vec3 ${m};
177
+ `,s[m]={value:new l.Vector3(...C)}):C.length===4&&(i+=`uniform vec4 ${m};
178
+ `,s[m]={value:new l.Vector4(...C)}):(i+=`uniform float ${m};
179
+ `,s[m]={value:C});const c=e!==void 0,u=(a||c?L.declChunk:"")+`
161
180
  `+i,d=`vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
162
- `,g=r.isTraveler?`vec2 resultUv = screenUv;
163
- `:`vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;
164
- `,y=a||s?d+g+((o==null?void 0:o.uvModifier)||""):"",m=a||s?E.baseColorChunk:"",h=(o==null?void 0:o.colorModifier)||"",R=G.fragmentShader.replace("#INJECT_DECLARATIONS",l).replace("#INJECT_UV_MODIFIER",y).replace("#INJECT_BASE_COLOR",m).replace("#INJECT_COLOR_MODIFIER",h),w=B(r.backgroundColor),C=B(r.borderColor),I={uSize:{value:new u.Vector2(e,t)},uBgColor:{value:new u.Vector4(w.color.r,w.color.g,w.color.b,w.alpha)},uBorderColor:{value:new u.Vector4(C.color.r,C.color.g,C.color.b,C.alpha)},uBorderRadius:{value:new u.Vector4(0,0,0,0)},uBorderWidth:{value:S(r.borderWidth)},uOpacity:{value:r.opacity??1},uTexture:{value:null},uTextureRepeat:{value:new u.Vector2(1,1)},uTextureOffset:{value:new u.Vector2(0,0)},uGradientCount:{value:0},uGradientAngle:{value:0},uGradientColors:{value:Array.from({length:8},()=>new u.Vector4(0,0,0,0))},uGradientStops:{value:new Float32Array(8)}};U(I.uBorderRadius.value,r.borderRadius,Math.min(e,t)),a&&(I.uTexture.value=n);const F=new u.ShaderMaterial({uniforms:{...I,...c},vertexShader:G.vertexShader,fragmentShader:R,transparent:!0,side:u.FrontSide});return r.backgroundImage&&O(F,{backgroundImage:r.backgroundImage}),F}function _(r,e,t,n,o){const a=S(e.borderWidth);O(r,{width:t,height:n,borderRadius:e.borderRadius,borderWidth:a,backgroundColor:e.backgroundColor,borderColor:e.borderColor,opacity:e.opacity,texture:o,backgroundImage:e.backgroundImage})}function O(r,e){var n;if(e.width!==void 0&&e.height!==void 0&&r.uniforms.uSize.value.set(e.width,e.height),e.borderRadius!==void 0){const o=e.width!==void 0&&e.height!==void 0?Math.min(e.width,e.height):Math.min(r.uniforms.uSize.value.x,r.uniforms.uSize.value.y);U(r.uniforms.uBorderRadius.value,e.borderRadius,o)}if(e.borderWidth!==void 0&&(r.uniforms.uBorderWidth.value=e.borderWidth),e.backgroundColor!==void 0)if(Array.isArray(e.backgroundColor)){const o=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(e.backgroundColor[0],e.backgroundColor[1],e.backgroundColor[2],o)}else if(typeof e.backgroundColor=="string"){const o=B(e.backgroundColor);r.uniforms.uBgColor.value.set(o.color.r,o.color.g,o.color.b,o.alpha)}else{const o=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(e.backgroundColor.r,e.backgroundColor.g,e.backgroundColor.b,o)}if(e.borderColor!==void 0)if(Array.isArray(e.borderColor)){const o=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(e.borderColor[0],e.borderColor[1],e.borderColor[2],o)}else if(typeof e.borderColor=="string"){const o=B(e.borderColor);r.uniforms.uBorderColor.value.set(o.color.r,o.color.g,o.color.b,o.alpha)}else{const o=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(e.borderColor.r,e.borderColor.g,e.borderColor.b,o)}e.opacity!==void 0&&(r.uniforms.uOpacity.value=e.opacity),e.bgOpacity!==void 0&&(r.uniforms.uBgColor.value.w=e.bgOpacity),e.borderOpacity!==void 0&&(r.uniforms.uBorderColor.value.w=e.borderOpacity),r.uniforms.uTexture&&e.texture!==void 0&&(r.uniforms.uTexture.value=e.texture);const t=e.texture!==void 0?e.texture:(n=r.uniforms.uTexture)==null?void 0:n.value;if(t&&(t.image instanceof ImageBitmap||t.image instanceof HTMLImageElement||t.image instanceof HTMLCanvasElement)){const o=t.image.naturalWidth||t.image.videoWidth||t.image.width,a=t.image.naturalHeight||t.image.videoHeight||t.image.height,i=e.width??r.uniforms.uSize.value.x,c=e.height??r.uniforms.uSize.value.y;if(o&&a&&i&&c){const s=o/a,l=i/c;if(s>l){const d=l/s;r.uniforms.uTextureRepeat.value.set(d,1),r.uniforms.uTextureOffset.value.set((1-d)/2,0)}else{const d=s/l;r.uniforms.uTextureRepeat.value.set(1,d),r.uniforms.uTextureOffset.value.set(0,(1-d)/2)}}}else r.uniforms.uTextureRepeat&&(r.uniforms.uTextureRepeat.value.set(1,1),r.uniforms.uTextureOffset.value.set(0,0));if(e.backgroundImage!==void 0){const o=k(e.backgroundImage);if(o){r.uniforms.uGradientCount.value=o.stops.length,r.uniforms.uGradientAngle.value=o.angle;for(let a=0;a<8;a++)if(a<o.stops.length){const i=o.stops[a];r.uniforms.uGradientColors.value[a].set(i.color.r,i.color.g,i.color.b,i.alpha),r.uniforms.uGradientStops.value[a]=i.stop}else r.uniforms.uGradientColors.value[a].set(0,0,0,0),r.uniforms.uGradientStops.value[a]=1}else r.uniforms.uGradientCount.value=0}for(const o of Object.keys(e))o!=="width"&&o!=="height"&&o!=="borderRadius"&&o!=="borderWidth"&&o!=="backgroundColor"&&o!=="borderColor"&&o!=="opacity"&&o!=="bgOpacity"&&o!=="borderOpacity"&&o!=="texture"&&o!=="backgroundImage"&&r.uniforms[o]!==void 0&&(r.uniforms[o].value!==void 0&&r.uniforms[o].value!==null&&typeof r.uniforms[o].value.set=="function"?Array.isArray(e[o])?r.uniforms[o].value.set(...e[o]):e[o]!==void 0&&(e[o].copy?r.uniforms[o].value.copy(e[o]):r.uniforms[o].value=e[o]):r.uniforms[o].value=e[o])}function U(r,e,t=0){if(e==null){r.set(0,0,0,0);return}if(typeof e=="number"){r.set(e,e,e,e);return}if(Array.isArray(e)){r.set(e[0],e[1],e[2],e[3]);return}const n=e.split("/")[0].trim().split(/\s+/),o=S(n[0],t),a=S(n[1]??n[0],t),i=S(n[2]??n[0],t),c=S(n[3]??n[1]??n[0],t);r.set(o,a,i,c)}const N={create(r,e,t,n,o,a=2,i=null,c){return r==="BOX"?L(e,n,o,i,c):r==="TEXT"?new A(t||"",e,n,o,a):new u.MeshBasicMaterial({visible:!1})},update(r,e,t,n,o,a,i=2,c){e==="BOX"?_(r,t,o,a,c):e==="TEXT"&&r.updateText(n||"",t,o,a,i)},forceUpdateUniforms(r,e){O(r,e)}};f.Painter=N,f.TextGenerator=A,f.createBoxMaterial=L,f.parseColor=B,f.parseLinearGradient=k,f.parsePixelValue=S,f.setBoxUniforms=O,f.splitByComma=P,f.updateBoxMaterial=_,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
181
+ `,b=r.isTraveler?`vec2 resultUv = screenUv;
182
+ `:`vec2 localUv = (p / uSize) + 0.5;
183
+ vec2 resultUv = localUv * uTextureRepeat + uTextureOffset;
184
+ `,v=a||c?d+b+((e==null?void 0:e.uvModifier)||""):"",h=a||c?L.baseColorChunk:"",g=(e==null?void 0:e.colorModifier)||"",M=E.fragmentShader.replace("#INJECT_DECLARATIONS",u).replace("#INJECT_UV_MODIFIER",v).replace("#INJECT_BASE_COLOR",h).replace("#INJECT_COLOR_MODIFIER",g),B=y(r.backgroundColor),x=y(r.borderColor),I={uSize:{value:new l.Vector2(o,n)},uMeshSize:{value:new l.Vector2(o,n)},uShadowColor:{value:new l.Vector4(0,0,0,0)},uShadowOffset:{value:new l.Vector2(0,0)},uShadowBlur:{value:0},uShadowSpread:{value:0},uBgColor:{value:new l.Vector4(B.color.r,B.color.g,B.color.b,B.alpha)},uBorderColor:{value:new l.Vector4(x.color.r,x.color.g,x.color.b,x.alpha)},uBorderRadius:{value:new l.Vector4(0,0,0,0)},uBorderWidth:{value:S(r.borderWidth)},uOpacity:{value:r.opacity??1},uTexture:{value:null},uTextureRepeat:{value:new l.Vector2(1,1)},uTextureOffset:{value:new l.Vector2(0,0)},uGradientCount:{value:0},uGradientAngle:{value:0},uGradientColors:{value:Array.from({length:8},()=>new l.Vector4(0,0,0,0))},uGradientStops:{value:new Float32Array(8)}};W(I.uBorderRadius.value,r.borderRadius,Math.min(o,n)),a&&(I.uTexture.value=t);const _=new l.ShaderMaterial({uniforms:{...I,...s},vertexShader:E.vertexShader,fragmentShader:M,transparent:!0,side:l.FrontSide});return r.backgroundImage&&O(_,{backgroundImage:r.backgroundImage}),_}function F(r,o,n,t,e){const a=S(o.borderWidth);O(r,{width:n,height:t,borderRadius:o.borderRadius,borderWidth:a,backgroundColor:o.backgroundColor,borderColor:o.borderColor,opacity:o.opacity,texture:e,backgroundImage:o.backgroundImage,boxShadow:o.boxShadow})}function O(r,o){var t;if(o.boxShadow!==void 0){const e=G(o.boxShadow);e?(r.uniforms.uShadowColor.value.set(e.color.r,e.color.g,e.color.b,e.alpha),r.uniforms.uShadowOffset.value.set(e.offsetX,e.offsetY),r.uniforms.uShadowBlur.value=e.blurRadius,r.uniforms.uShadowSpread.value=e.spreadRadius,r.userData.shadowPadding=e.blurRadius+e.spreadRadius+Math.max(Math.abs(e.offsetX),Math.abs(e.offsetY))):(r.uniforms.uShadowColor.value.w=0,r.userData.shadowPadding=0)}if(o.width!==void 0&&o.height!==void 0&&r.uniforms.uSize.value.set(o.width,o.height),r.uniforms.uMeshSize){const e=r.userData.shadowPadding||0,a=o.width!==void 0?o.width:r.uniforms.uSize.value.x,i=o.height!==void 0?o.height:r.uniforms.uSize.value.y;r.uniforms.uMeshSize.value.set(a+e*2,i+e*2)}if(o.borderRadius!==void 0){const e=o.width!==void 0&&o.height!==void 0?Math.min(o.width,o.height):Math.min(r.uniforms.uSize.value.x,r.uniforms.uSize.value.y);W(r.uniforms.uBorderRadius.value,o.borderRadius,e)}if(o.borderWidth!==void 0&&(r.uniforms.uBorderWidth.value=o.borderWidth),o.backgroundColor!==void 0)if(Array.isArray(o.backgroundColor)){const e=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(o.backgroundColor[0],o.backgroundColor[1],o.backgroundColor[2],e)}else if(typeof o.backgroundColor=="string"){const e=y(o.backgroundColor);r.uniforms.uBgColor.value.set(e.color.r,e.color.g,e.color.b,e.alpha)}else{const e=r.uniforms.uBgColor.value.w;r.uniforms.uBgColor.value.set(o.backgroundColor.r,o.backgroundColor.g,o.backgroundColor.b,e)}if(o.borderColor!==void 0)if(Array.isArray(o.borderColor)){const e=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(o.borderColor[0],o.borderColor[1],o.borderColor[2],e)}else if(typeof o.borderColor=="string"){const e=y(o.borderColor);r.uniforms.uBorderColor.value.set(e.color.r,e.color.g,e.color.b,e.alpha)}else{const e=r.uniforms.uBorderColor.value.w;r.uniforms.uBorderColor.value.set(o.borderColor.r,o.borderColor.g,o.borderColor.b,e)}o.opacity!==void 0&&(r.uniforms.uOpacity.value=o.opacity),o.bgOpacity!==void 0&&(r.uniforms.uBgColor.value.w=o.bgOpacity),o.borderOpacity!==void 0&&(r.uniforms.uBorderColor.value.w=o.borderOpacity),r.uniforms.uTexture&&o.texture!==void 0&&(r.uniforms.uTexture.value=o.texture);const n=o.texture!==void 0?o.texture:(t=r.uniforms.uTexture)==null?void 0:t.value;if(n&&(n.image instanceof ImageBitmap||n.image instanceof HTMLImageElement||n.image instanceof HTMLCanvasElement)){const e=n.image.naturalWidth||n.image.videoWidth||n.image.width,a=n.image.naturalHeight||n.image.videoHeight||n.image.height,i=o.width??r.uniforms.uSize.value.x,s=o.height??r.uniforms.uSize.value.y;if(e&&a&&i&&s){const c=e/a,u=i/s;if(c>u){const d=u/c;r.uniforms.uTextureRepeat.value.set(d,1),r.uniforms.uTextureOffset.value.set((1-d)/2,0)}else{const d=c/u;r.uniforms.uTextureRepeat.value.set(1,d),r.uniforms.uTextureOffset.value.set(0,(1-d)/2)}}}else r.uniforms.uTextureRepeat&&(r.uniforms.uTextureRepeat.value.set(1,1),r.uniforms.uTextureOffset.value.set(0,0));if(o.backgroundImage!==void 0){const e=k(o.backgroundImage);if(e){r.uniforms.uGradientCount.value=e.stops.length,r.uniforms.uGradientAngle.value=e.angle;for(let a=0;a<8;a++)if(a<e.stops.length){const i=e.stops[a];r.uniforms.uGradientColors.value[a].set(i.color.r,i.color.g,i.color.b,i.alpha),r.uniforms.uGradientStops.value[a]=i.stop}else r.uniforms.uGradientColors.value[a].set(0,0,0,0),r.uniforms.uGradientStops.value[a]=1}else r.uniforms.uGradientCount.value=0}for(const e of Object.keys(o))e!=="width"&&e!=="height"&&e!=="borderRadius"&&e!=="borderWidth"&&e!=="backgroundColor"&&e!=="borderColor"&&e!=="opacity"&&e!=="bgOpacity"&&e!=="borderOpacity"&&e!=="texture"&&e!=="backgroundImage"&&e!=="boxShadow"&&r.uniforms[e]!==void 0&&(r.uniforms[e].value!==void 0&&r.uniforms[e].value!==null&&typeof r.uniforms[e].value.set=="function"?Array.isArray(o[e])?r.uniforms[e].value.set(...o[e]):o[e]!==void 0&&(o[e].copy?r.uniforms[e].value.copy(o[e]):r.uniforms[e].value=o[e]):r.uniforms[e].value=o[e])}function W(r,o,n=0){if(o==null){r.set(0,0,0,0);return}if(typeof o=="number"){r.set(o,o,o,o);return}if(Array.isArray(o)){r.set(o[0],o[1],o[2],o[3]);return}const t=o.split("/")[0].trim().split(/\s+/),e=S(t[0],n),a=S(t[1]??t[0],n),i=S(t[2]??t[0],n),s=S(t[3]??t[1]??t[0],n);r.set(e,a,i,s)}const q={create(r,o,n,t,e,a=2,i=null,s){return r==="BOX"?z(o,t,e,i,s):r==="TEXT"?new A(n||"",o,t,e,a):new l.MeshBasicMaterial({visible:!1})},update(r,o,n,t,e,a,i=2,s){o==="BOX"?F(r,n,e,a,s):o==="TEXT"&&r.updateText(t||"",n,e,a,i)},forceUpdateUniforms(r,o){O(r,o)}};f.Painter=q,f.TextGenerator=A,f.createBoxMaterial=z,f.parseBoxShadow=G,f.parseColor=y,f.parseLinearGradient=k,f.parsePixelValue=S,f.setBoxUniforms=O,f.splitByComma=R,f.updateBoxMaterial=F,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})});
@@ -14,6 +14,7 @@ export interface BoxUniformValues {
14
14
  borderOpacity?: number;
15
15
  texture?: THREE.Texture | null;
16
16
  backgroundImage?: string;
17
+ boxShadow?: string;
17
18
  [key: string]: any;
18
19
  }
19
20
  export declare function setBoxUniforms(material: THREE.ShaderMaterial, values: BoxUniformValues): void;
@@ -14,3 +14,11 @@ export declare function parseLinearGradient(value: string | null | undefined): {
14
14
  stop: number;
15
15
  }[];
16
16
  } | null;
17
+ export declare function parseBoxShadow(value: string | null | undefined): {
18
+ offsetX: number;
19
+ offsetY: number;
20
+ blurRadius: number;
21
+ spreadRadius: number;
22
+ color: THREE.Color;
23
+ alpha: number;
24
+ } | null;
@@ -20,6 +20,7 @@ export interface BoxStyles {
20
20
  borderColor: string;
21
21
  borderWidth: string;
22
22
  transform?: string;
23
+ boxShadow?: string;
23
24
  }
24
25
  export interface ShaderHooks {
25
26
  uvModifier?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mirage-engine/painter",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "main": "src/index.ts",
5
5
  "types": "src/index.ts",
6
6
  "peerDependencies": {
@@ -4,6 +4,7 @@ import {
4
4
  parsePixelValue,
5
5
  parseColor,
6
6
  parseLinearGradient,
7
+ parseBoxShadow,
7
8
  } from "../tools/parser";
8
9
  import { BoxShader, BoxChunk } from "../shaders/index";
9
10
 
@@ -45,7 +46,7 @@ export function createBoxMaterial(
45
46
  const hasHooks = hooks !== undefined;
46
47
  const declChunk = (hasTexture || hasHooks ? BoxChunk.declChunk : "") + "\n" + customUniformsCode;
47
48
  const screenUvDecl = "vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;\n";
48
- const baseUvCode = styles.isTraveler ? "vec2 resultUv = screenUv;\n" : "vec2 resultUv = vUv * uTextureRepeat + uTextureOffset;\n";
49
+ const baseUvCode = styles.isTraveler ? "vec2 resultUv = screenUv;\n" : "vec2 localUv = (p / uSize) + 0.5;\nvec2 resultUv = localUv * uTextureRepeat + uTextureOffset;\n";
49
50
  const uvChunk = (hasTexture || hasHooks) ? screenUvDecl + baseUvCode + (hooks?.uvModifier || "") : "";
50
51
  const baseColorChunk = (hasTexture || hasHooks) ? BoxChunk.baseColorChunk : "";
51
52
  const colorModChunk = hooks?.colorModifier || "";
@@ -61,6 +62,11 @@ export function createBoxMaterial(
61
62
  const parsedBorder = parseColor(styles.borderColor);
62
63
  const uniforms = {
63
64
  uSize: { value: new THREE.Vector2(width, height) },
65
+ uMeshSize: { value: new THREE.Vector2(width, height) },
66
+ uShadowColor: { value: new THREE.Vector4(0, 0, 0, 0) },
67
+ uShadowOffset: { value: new THREE.Vector2(0, 0) },
68
+ uShadowBlur: { value: 0 },
69
+ uShadowSpread: { value: 0 },
64
70
  uBgColor: {
65
71
  value: new THREE.Vector4(
66
72
  parsedBg.color.r,
@@ -132,6 +138,7 @@ export function updateBoxMaterial(
132
138
  opacity: styles.opacity,
133
139
  texture,
134
140
  backgroundImage: styles.backgroundImage,
141
+ boxShadow: styles.boxShadow,
135
142
  });
136
143
  }
137
144
 
@@ -147,6 +154,7 @@ export interface BoxUniformValues {
147
154
  borderOpacity?: number;
148
155
  texture?: THREE.Texture | null;
149
156
  backgroundImage?: string;
157
+ boxShadow?: string;
150
158
  [key: string]: any;
151
159
  }
152
160
 
@@ -154,9 +162,31 @@ export function setBoxUniforms(
154
162
  material: THREE.ShaderMaterial,
155
163
  values: BoxUniformValues,
156
164
  ) {
165
+ if (values.boxShadow !== undefined) {
166
+ const shadow = parseBoxShadow(values.boxShadow);
167
+ if (shadow) {
168
+ material.uniforms.uShadowColor.value.set(shadow.color.r, shadow.color.g, shadow.color.b, shadow.alpha);
169
+ material.uniforms.uShadowOffset.value.set(shadow.offsetX, shadow.offsetY);
170
+ material.uniforms.uShadowBlur.value = shadow.blurRadius;
171
+ material.uniforms.uShadowSpread.value = shadow.spreadRadius;
172
+ material.userData.shadowPadding = shadow.blurRadius + shadow.spreadRadius + Math.max(Math.abs(shadow.offsetX), Math.abs(shadow.offsetY));
173
+ } else {
174
+ material.uniforms.uShadowColor.value.w = 0;
175
+ material.userData.shadowPadding = 0;
176
+ }
177
+ }
178
+
157
179
  if (values.width !== undefined && values.height !== undefined) {
158
180
  material.uniforms.uSize.value.set(values.width, values.height);
159
181
  }
182
+
183
+ if (material.uniforms.uMeshSize) {
184
+ const pad = material.userData.shadowPadding || 0;
185
+ const cw = values.width !== undefined ? values.width : material.uniforms.uSize.value.x;
186
+ const ch = values.height !== undefined ? values.height : material.uniforms.uSize.value.y;
187
+ material.uniforms.uMeshSize.value.set(cw + pad * 2, ch + pad * 2);
188
+ }
189
+
160
190
  if (values.borderRadius !== undefined) {
161
191
  const baseSize = values.width !== undefined && values.height !== undefined
162
192
  ? Math.min(values.width, values.height)
@@ -301,7 +331,8 @@ export function setBoxUniforms(
301
331
  key !== "bgOpacity" &&
302
332
  key !== "borderOpacity" &&
303
333
  key !== "texture" &&
304
- key !== "backgroundImage"
334
+ key !== "backgroundImage" &&
335
+ key !== "boxShadow"
305
336
  ) {
306
337
  if (material.uniforms[key] !== undefined) {
307
338
  if (
@@ -1,5 +1,10 @@
1
1
  varying vec2 vUv;
2
2
  uniform vec2 uSize;
3
+ uniform vec2 uMeshSize;
4
+ uniform vec4 uShadowColor;
5
+ uniform vec2 uShadowOffset;
6
+ uniform float uShadowBlur;
7
+ uniform float uShadowSpread;
3
8
  uniform vec4 uBorderRadius;
4
9
  uniform float uBorderWidth;
5
10
  uniform vec4 uBgColor;
@@ -55,11 +60,10 @@ float sdVisualBox(vec2 p, vec2 b, float r) {
55
60
  return min(max(q.x, q.y), 0.0) + d;
56
61
  }
57
62
 
58
- vec4 calculateGradientLayer(vec2 uv) {
63
+ vec4 calculateGradientLayer(vec2 p) {
59
64
  if (uGradientCount < 2) return uGradientColors[0];
60
65
 
61
66
  vec2 dir = vec2(sin(uGradientAngle), cos(uGradientAngle));
62
- vec2 p = (uv - 0.5) * uSize;
63
67
  float proj = dot(p, dir);
64
68
  float L = abs(uSize.x * dir.x) + abs(uSize.y * dir.y);
65
69
  float t = clamp((proj / L) + 0.5, 0.0, 1.0);
@@ -78,7 +82,7 @@ vec4 calculateGradientLayer(vec2 uv) {
78
82
  }
79
83
 
80
84
  void main() {
81
- vec2 p = (vUv - 0.5) * uSize;
85
+ vec2 p = (vUv - 0.5) * uMeshSize;
82
86
  vec2 halfSize = uSize * 0.5;
83
87
 
84
88
  // CSS Proportional Border-Radius Clamping
@@ -99,7 +103,7 @@ void main() {
99
103
  vec4 baseColor = vec4(uBgColor.rgb, uBgColor.a);
100
104
 
101
105
  if (uGradientCount > 0) {
102
- vec4 gradColor = calculateGradientLayer(vUv);
106
+ vec4 gradColor = calculateGradientLayer(p);
103
107
  baseColor = blendSrcOver(gradColor, baseColor);
104
108
  }
105
109
 
@@ -126,11 +130,26 @@ void main() {
126
130
 
127
131
  // final blending (border + background) using blendSrcOver
128
132
  vec4 borderLayer = vec4(uBorderColor.rgb, borderAlpha);
129
- vec4 finalColor = blendSrcOver(borderLayer, baseColor);
133
+ vec4 mainColor = blendSrcOver(borderLayer, baseColor);
134
+ mainColor.a *= bgMask;
135
+
136
+ vec4 shadowLayer = vec4(0.0);
137
+ if (uShadowColor.a > 0.001) {
138
+ vec2 shadowP = p - vec2(uShadowOffset.x, -uShadowOffset.y);
139
+ float shadowRadius = max(r + uShadowSpread, 0.0);
140
+ vec2 shadowHalfSize = max(halfSize + vec2(uShadowSpread), vec2(0.0));
141
+ float shadowD = sdRoundedBox(shadowP, shadowHalfSize, shadowRadius);
142
+ float blur = max(uShadowBlur, 1.0);
143
+ float shadowMask = 1.0 - smoothstep(-blur * 0.5, blur * 0.5, shadowD);
144
+ shadowLayer = vec4(uShadowColor.rgb, uShadowColor.a * shadowMask);
145
+ }
146
+
147
+ vec4 finalColor = blendSrcOver(mainColor, shadowLayer);
148
+
130
149
  // final color control (Tint, Noise)
131
150
  #INJECT_COLOR_MODIFIER
132
151
 
133
- float finalOpacity = finalColor.a * bgMask * uOpacity;
152
+ float finalOpacity = finalColor.a * uOpacity;
134
153
  if (finalOpacity < 0.001) discard;
135
154
 
136
155
  gl_FragColor = vec4(finalColor.rgb, finalOpacity);
@@ -150,3 +150,37 @@ export function parseLinearGradient(value: string | null | undefined) {
150
150
 
151
151
  return { angle, stops };
152
152
  }
153
+
154
+ export function parseBoxShadow(value: string | null | undefined) {
155
+ if (!value || value === "none") return null;
156
+
157
+ const shadows = splitByComma(value);
158
+ if (shadows.length === 0) return null;
159
+ const shadowStr = shadows[0].trim();
160
+
161
+ const colorRegex = /(rgba?\([^)]+\)|#[0-9a-fA-F]+|[a-zA-Z]+)/;
162
+ const colorMatch = shadowStr.match(colorRegex);
163
+
164
+ let colorStr = "transparent";
165
+ let restStr = shadowStr;
166
+
167
+ if (colorMatch && colorMatch[1] !== 'inset') {
168
+ const c = colorMatch[1];
169
+ if (c.startsWith("rgb") || c.startsWith("#") || c === "black" || c === "white" || c === "transparent") {
170
+ colorStr = c;
171
+ restStr = shadowStr.replace(c, "").trim();
172
+ }
173
+ }
174
+
175
+ restStr = restStr.replace("inset", "").trim();
176
+ const lengths = restStr.split(/\s+/).map(s => parseFloat(s) || 0);
177
+
178
+ const offsetX = lengths[0] || 0;
179
+ const offsetY = lengths[1] || 0;
180
+ const blurRadius = lengths[2] || 0;
181
+ const spreadRadius = lengths[3] || 0;
182
+
183
+ const color = parseColor(colorStr);
184
+
185
+ return { offsetX, offsetY, blurRadius, spreadRadius, color: color.color, alpha: color.alpha };
186
+ }
@@ -0,0 +1,39 @@
1
+ --- packages/painter/src/tools/parser.ts
2
+ +++ packages/painter/src/tools/parser.ts
3
+ @@ -194,2 +194,37 @@
4
+ return { angle, stops };
5
+ }
6
+ +
7
+ +export function parseBoxShadow(value: string | null | undefined) {
8
+ + if (!value || value === "none") return null;
9
+ +
10
+ + const shadows = splitByComma(value);
11
+ + if (shadows.length === 0) return null;
12
+ + const shadowStr = shadows[0].trim();
13
+ +
14
+ + const colorRegex = /(rgba?\([^)]+\)|#[0-9a-fA-F]+|[a-zA-Z]+)/;
15
+ + const colorMatch = shadowStr.match(colorRegex);
16
+ +
17
+ + let colorStr = "transparent";
18
+ + let restStr = shadowStr;
19
+ +
20
+ + if (colorMatch && colorMatch[1] !== 'inset') {
21
+ + const c = colorMatch[1];
22
+ + if (c.startsWith("rgb") || c.startsWith("#") || c === "black" || c === "white" || c === "transparent") {
23
+ + colorStr = c;
24
+ + restStr = shadowStr.replace(c, "").trim();
25
+ + }
26
+ + }
27
+ +
28
+ + restStr = restStr.replace("inset", "").trim();
29
+ + const lengths = restStr.split(/\s+/).map(s => parseFloat(s) || 0);
30
+ +
31
+ + const offsetX = lengths[0] || 0;
32
+ + const offsetY = lengths[1] || 0;
33
+ + const blurRadius = lengths[2] || 0;
34
+ + const spreadRadius = lengths[3] || 0;
35
+ +
36
+ + const color = parseColor(colorStr);
37
+ +
38
+ + return { offsetX, offsetY, blurRadius, spreadRadius, color: color.color, alpha: color.alpha };
39
+ +}
@@ -22,6 +22,7 @@ export interface BoxStyles {
22
22
  borderColor: string;
23
23
  borderWidth: string;
24
24
  transform?: string;
25
+ boxShadow?: string;
25
26
  }
26
27
 
27
28
  export interface ShaderHooks {