@mirage-engine/painter 0.2.3 → 0.3.0
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 +6 -0
- package/dist/mirage-painter.js +159 -103
- package/dist/mirage-painter.umd.js +57 -27
- package/dist/src/BoxGenerator.d.ts +5 -2
- package/dist/src/Painter.d.ts +2 -2
- package/dist/src/dev/devShader.d.ts +0 -0
- package/package.json +1 -1
- package/src/BoxGenerator.ts +112 -30
- package/src/Painter.ts +4 -1
- package/src/dev/devShader.ts +0 -0
package/CHANGELOG.md
CHANGED
package/dist/mirage-painter.js
CHANGED
|
@@ -1,138 +1,193 @@
|
|
|
1
1
|
import * as l from "three";
|
|
2
|
-
function
|
|
3
|
-
const
|
|
4
|
-
`),
|
|
5
|
-
return
|
|
6
|
-
const
|
|
7
|
-
let
|
|
8
|
-
for (let
|
|
9
|
-
const
|
|
10
|
-
|
|
2
|
+
function h(e, r, o) {
|
|
3
|
+
const i = r.split(`
|
|
4
|
+
`), a = [];
|
|
5
|
+
return i.forEach((n) => {
|
|
6
|
+
const t = n.split(" ");
|
|
7
|
+
let c = t[0];
|
|
8
|
+
for (let u = 1; u < t.length; u++) {
|
|
9
|
+
const s = t[u];
|
|
10
|
+
e.measureText(c + " " + s).width < o ? c += " " + s : (a.push(c), c = s);
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
}),
|
|
12
|
+
a.push(c);
|
|
13
|
+
}), a;
|
|
14
14
|
}
|
|
15
|
-
function m(
|
|
16
|
-
const
|
|
17
|
-
if (!
|
|
15
|
+
function m(e, r, o, i, a = 2) {
|
|
16
|
+
const n = document.createElement("canvas"), t = n.getContext("2d");
|
|
17
|
+
if (!t)
|
|
18
18
|
throw new Error("[Mirage] Failed to create canvas context");
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
let
|
|
25
|
-
|
|
19
|
+
const u = (window.devicePixelRatio || 1) * a;
|
|
20
|
+
n.width = o * u, n.height = i * u, t.scale(u, u), t.font = r.font, t.fillStyle = r.color, t.textBaseline = "top", t.globalAlpha = 1;
|
|
21
|
+
const s = h(t, e, o), v = r.lineHeight;
|
|
22
|
+
s.forEach((B, p) => {
|
|
23
|
+
const g = p * v + 2;
|
|
24
|
+
let C = 0;
|
|
25
|
+
r.textAlign === "center" ? C = o / 2 : r.textAlign === "right" && (C = o), t.textAlign = r.textAlign, t.fillText(B, C, g);
|
|
26
26
|
});
|
|
27
|
-
const
|
|
28
|
-
return
|
|
27
|
+
const d = new l.CanvasTexture(n);
|
|
28
|
+
return d.colorSpace = l.SRGBColorSpace, d.minFilter = l.LinearFilter, d.magFilter = l.LinearFilter, d.needsUpdate = !0, d;
|
|
29
29
|
}
|
|
30
|
-
function
|
|
31
|
-
return typeof
|
|
30
|
+
function f(e) {
|
|
31
|
+
return typeof e == "number" ? e : parseFloat(e) || 0;
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
function b(e, r) {
|
|
34
|
+
if (!r) {
|
|
35
|
+
e.set(0, 0, 0, 0);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const o = r.split("/")[0].trim().split(/\s+/), i = f(o[0]), a = f(o[1] ?? o[0]), n = f(o[2] ?? o[0]), t = f(o[3] ?? o[1] ?? o[0]);
|
|
39
|
+
e.set(i, a, n, t);
|
|
40
|
+
}
|
|
41
|
+
const O = (
|
|
42
|
+
/* glsl */
|
|
43
|
+
`
|
|
34
44
|
varying vec2 vUv;
|
|
45
|
+
varying vec4 vScreenPos;
|
|
35
46
|
void main() {
|
|
36
47
|
vUv = uv;
|
|
37
48
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
49
|
+
vScreenPos = gl_Position;
|
|
38
50
|
}
|
|
39
|
-
|
|
51
|
+
`
|
|
52
|
+
), T = (
|
|
53
|
+
/* glsl */
|
|
54
|
+
`
|
|
55
|
+
|
|
40
56
|
varying vec2 vUv;
|
|
41
57
|
|
|
42
58
|
uniform vec2 uSize;
|
|
43
|
-
uniform
|
|
59
|
+
uniform vec4 uBorderRadius;
|
|
44
60
|
uniform float uBorderWidth;
|
|
45
|
-
uniform vec3
|
|
61
|
+
uniform vec3 uBgColor;
|
|
46
62
|
uniform vec3 uBorderColor;
|
|
47
63
|
uniform float uOpacity;
|
|
48
64
|
uniform float uBgOpacity;
|
|
65
|
+
uniform float uBorderOpacity;
|
|
66
|
+
|
|
67
|
+
#INJECT_DECLARATIONS
|
|
49
68
|
|
|
50
|
-
// SDF 박스 함수
|
|
51
69
|
float sdRoundedBox(vec2 p, vec2 b, float r) {
|
|
52
70
|
vec2 q = abs(p) - b + r;
|
|
71
|
+
|
|
53
72
|
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
|
54
73
|
}
|
|
55
74
|
|
|
56
75
|
void main() {
|
|
57
76
|
vec2 p = (vUv - 0.5) * uSize;
|
|
58
77
|
vec2 halfSize = uSize * 0.5;
|
|
59
|
-
|
|
60
|
-
float d = sdRoundedBox(p, halfSize, uRadius);
|
|
61
|
-
|
|
62
|
-
float smoothEdge = 1.0;
|
|
63
78
|
|
|
64
|
-
|
|
79
|
+
#INJECT_UV_MODIFIER
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (uBorderWidth > 0.01) {
|
|
69
|
-
borderAlpha = (1.0 - smoothstep(0.0, smoothEdge, d)) - fillAlpha;
|
|
70
|
-
}
|
|
81
|
+
// color decision pipeline
|
|
82
|
+
vec4 baseColor = vec4(uBgColor, uBgOpacity);
|
|
71
83
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
#INJECT_BASE_COLOR
|
|
85
|
+
|
|
86
|
+
// sdf shape pipeline
|
|
87
|
+
vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
|
|
88
|
+
float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
|
|
89
|
+
float d = sdRoundedBox(p, halfSize, r);
|
|
90
|
+
|
|
91
|
+
float aa = 1.0;
|
|
92
|
+
float bgMask = 1.0 - smoothstep(0.0, aa, d);
|
|
93
|
+
|
|
94
|
+
float halfBorder = uBorderWidth * 0.5;
|
|
95
|
+
float borderD = abs(d + halfBorder) - halfBorder;
|
|
96
|
+
float borderAlpha = (1.0 - smoothstep(0.0, aa, borderD)) * uBorderOpacity;
|
|
97
|
+
if (uBorderWidth <= 0.01) {
|
|
98
|
+
borderAlpha = 0.0;
|
|
77
99
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
float
|
|
81
|
-
|
|
100
|
+
|
|
101
|
+
// final blending (border + background)
|
|
102
|
+
float aFront = borderAlpha;
|
|
103
|
+
float aBack = baseColor.a;
|
|
104
|
+
float aOut = aFront + aBack * (1.0 - aFront);
|
|
105
|
+
|
|
106
|
+
float safeAlpha = max(aOut, 0.0001);
|
|
107
|
+
vec3 cOut = (uBorderColor * aFront + baseColor.rgb * aBack * (1.0 - aFront)) / safeAlpha;
|
|
108
|
+
vec4 finalColor = vec4(cOut, aOut);
|
|
109
|
+
|
|
110
|
+
// final color control (Tint, Noise)
|
|
111
|
+
#INJECT_COLOR_MODIFIER
|
|
112
|
+
|
|
113
|
+
float finalOpacity = finalColor.a * bgMask * uOpacity;
|
|
82
114
|
if (finalOpacity < 0.001) discard;
|
|
83
115
|
|
|
84
|
-
gl_FragColor = vec4(
|
|
116
|
+
gl_FragColor = vec4(finalColor.rgb, finalOpacity);
|
|
117
|
+
|
|
118
|
+
#include <colorspace_fragment>
|
|
85
119
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
120
|
+
`
|
|
121
|
+
);
|
|
122
|
+
function x(e) {
|
|
123
|
+
if (!e || e === "transparent")
|
|
89
124
|
return { color: new l.Color(16777215), alpha: 0 };
|
|
90
|
-
const
|
|
125
|
+
const r = e.match(
|
|
91
126
|
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
|
|
92
127
|
);
|
|
93
|
-
if (
|
|
94
|
-
const
|
|
95
|
-
return { color: new l.Color(`rgb(${
|
|
128
|
+
if (r) {
|
|
129
|
+
const o = parseInt(r[1], 10), i = parseInt(r[2], 10), a = parseInt(r[3], 10), n = r[4] !== void 0 ? parseFloat(r[4]) : 1;
|
|
130
|
+
return { color: new l.Color(`rgb(${o}, ${i}, ${a})`), alpha: n };
|
|
96
131
|
}
|
|
97
|
-
return { color: new l.Color(
|
|
132
|
+
return { color: new l.Color(e), alpha: 1 };
|
|
98
133
|
}
|
|
99
|
-
function
|
|
100
|
-
const n =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
134
|
+
function R(e, r, o, i = null, a) {
|
|
135
|
+
const n = i !== null, t = n ? (
|
|
136
|
+
/* glsl */
|
|
137
|
+
`
|
|
138
|
+
uniform sampler2D uTexture;
|
|
139
|
+
varying vec4 vScreenPos;
|
|
140
|
+
`
|
|
141
|
+
) : "", c = n ? (
|
|
142
|
+
/* glsl */
|
|
143
|
+
`
|
|
144
|
+
vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
145
|
+
vec2 resultUv = screenUv;
|
|
146
|
+
${(a == null ? void 0 : a.uvModifier) || ""}
|
|
147
|
+
`
|
|
148
|
+
) : "", u = n ? (
|
|
149
|
+
/* glsl */
|
|
150
|
+
`
|
|
151
|
+
baseColor = texture2D(uTexture, resultUv);
|
|
152
|
+
`
|
|
153
|
+
) : "", s = (a == null ? void 0 : a.colorModifier) || "", v = T.replace("#INJECT_DECLARATIONS", t).replace("#INJECT_UV_MODIFIER", c).replace("#INJECT_BASE_COLOR", u).replace("#INJECT_COLOR_MODIFIER", s), d = x(e.backgroundColor), B = x(e.borderColor), p = {
|
|
154
|
+
uSize: { value: new l.Vector2(r, o) },
|
|
155
|
+
uBorderRadius: { value: new l.Vector4(0, 0, 0, 0) },
|
|
156
|
+
uBorderWidth: { value: f(e.borderWidth) },
|
|
157
|
+
uBgColor: { value: d.color },
|
|
158
|
+
uBorderColor: { value: B.color },
|
|
159
|
+
uOpacity: { value: e.opacity ?? 1 },
|
|
160
|
+
uBgOpacity: { value: d.alpha },
|
|
161
|
+
uBorderOpacity: { value: B.alpha },
|
|
162
|
+
uTexture: { value: null }
|
|
108
163
|
};
|
|
109
|
-
return new l.ShaderMaterial({
|
|
110
|
-
uniforms:
|
|
111
|
-
vertexShader:
|
|
112
|
-
fragmentShader:
|
|
164
|
+
return b(p.uBorderRadius.value, e.borderRadius), n && (p.uTexture.value = i), new l.ShaderMaterial({
|
|
165
|
+
uniforms: p,
|
|
166
|
+
vertexShader: O,
|
|
167
|
+
fragmentShader: v,
|
|
113
168
|
transparent: !0,
|
|
114
169
|
side: l.FrontSide
|
|
115
170
|
// for better performance
|
|
116
171
|
});
|
|
117
172
|
}
|
|
118
|
-
function
|
|
119
|
-
const
|
|
120
|
-
|
|
173
|
+
function E(e, r, o, i, a) {
|
|
174
|
+
const n = x(r.backgroundColor), t = x(r.borderColor);
|
|
175
|
+
e.uniforms.uSize.value.set(o, i), b(e.uniforms.uBorderRadius.value, r.borderRadius), e.uniforms.uBorderWidth.value = f(r.borderWidth), e.uniforms.uBgColor.value.copy(n.color), e.uniforms.uBorderColor.value.copy(t.color), e.uniforms.uOpacity.value = r.opacity ?? 1, e.uniforms.uBgOpacity.value = n.alpha, e.uniforms.uBorderOpacity.value = t.alpha, e.uniforms.uTexture && a !== void 0 && (e.uniforms.uTexture.value = a);
|
|
121
176
|
}
|
|
122
|
-
const
|
|
123
|
-
create(o,
|
|
124
|
-
if (
|
|
125
|
-
return
|
|
126
|
-
if (
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
e,
|
|
130
|
-
n,
|
|
177
|
+
const S = {
|
|
178
|
+
create(e, r, o, i, a, n = 2, t = null) {
|
|
179
|
+
if (e === "BOX")
|
|
180
|
+
return R(r, i, a, t);
|
|
181
|
+
if (e === "TEXT") {
|
|
182
|
+
const c = m(
|
|
183
|
+
o || "",
|
|
131
184
|
r,
|
|
132
|
-
i
|
|
185
|
+
i,
|
|
186
|
+
a,
|
|
187
|
+
n
|
|
133
188
|
);
|
|
134
189
|
return new l.MeshBasicMaterial({
|
|
135
|
-
map:
|
|
190
|
+
map: c,
|
|
136
191
|
transparent: !0,
|
|
137
192
|
side: l.FrontSide,
|
|
138
193
|
color: 16777215
|
|
@@ -140,31 +195,32 @@ const O = {
|
|
|
140
195
|
}
|
|
141
196
|
return new l.MeshBasicMaterial({ visible: !1 });
|
|
142
197
|
},
|
|
143
|
-
update(
|
|
144
|
-
if (
|
|
145
|
-
|
|
198
|
+
update(e, r, o, i, a, n, t = 2, c) {
|
|
199
|
+
if (r === "BOX")
|
|
200
|
+
E(
|
|
201
|
+
e,
|
|
146
202
|
o,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
203
|
+
a,
|
|
204
|
+
n,
|
|
205
|
+
c
|
|
150
206
|
);
|
|
151
|
-
else if (
|
|
152
|
-
const u =
|
|
207
|
+
else if (r === "TEXT") {
|
|
208
|
+
const u = e;
|
|
153
209
|
u.map && u.map.dispose();
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
210
|
+
const s = m(
|
|
211
|
+
i || "",
|
|
212
|
+
o,
|
|
213
|
+
a,
|
|
214
|
+
n,
|
|
215
|
+
t
|
|
160
216
|
);
|
|
161
|
-
u.map =
|
|
217
|
+
u.map = s, u.needsUpdate = !0;
|
|
162
218
|
}
|
|
163
219
|
}
|
|
164
220
|
};
|
|
165
221
|
export {
|
|
166
|
-
|
|
167
|
-
|
|
222
|
+
S as Painter,
|
|
223
|
+
R as createBoxMaterial,
|
|
168
224
|
m as createTextTexture,
|
|
169
|
-
|
|
225
|
+
E as updateBoxMaterial
|
|
170
226
|
};
|
|
@@ -1,55 +1,85 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
`),
|
|
1
|
+
(function(s,v){typeof exports=="object"&&typeof module<"u"?v(exports,require("three")):typeof define=="function"&&define.amd?define(["exports","three"],v):(s=typeof globalThis<"u"?globalThis:s||self,v(s.MiragePainter={},s.THREE))})(this,function(s,v){"use strict";function S(e){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const o in e)if(o!=="default"){const i=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(r,o,i.get?i:{enumerable:!0,get:()=>e[o]})}}return r.default=e,Object.freeze(r)}const c=S(v);function E(e,r,o){const i=r.split(`
|
|
2
|
+
`),t=[];return i.forEach(n=>{const a=n.split(" ");let l=a[0];for(let u=1;u<a.length;u++){const d=a[u];e.measureText(l+" "+d).width<o?l+=" "+d:(t.push(l),l=d)}t.push(l)}),t}function m(e,r,o,i,t=2){const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)throw new Error("[Mirage] Failed to create canvas context");const u=(window.devicePixelRatio||1)*t;n.width=o*u,n.height=i*u,a.scale(u,u),a.font=r.font,a.fillStyle=r.color,a.textBaseline="top",a.globalAlpha=1;const d=E(a,e,o),x=r.lineHeight;d.forEach((b,B)=>{const R=B*x+2;let C=0;r.textAlign==="center"?C=o/2:r.textAlign==="right"&&(C=o),a.textAlign=r.textAlign,a.fillText(b,C,R)});const f=new c.CanvasTexture(n);return f.colorSpace=c.SRGBColorSpace,f.minFilter=c.LinearFilter,f.magFilter=c.LinearFilter,f.needsUpdate=!0,f}function p(e){return typeof e=="number"?e:parseFloat(e)||0}function h(e,r){if(!r){e.set(0,0,0,0);return}const o=r.split("/")[0].trim().split(/\s+/),i=p(o[0]),t=p(o[1]??o[0]),n=p(o[2]??o[0]),a=p(o[3]??o[1]??o[0]);e.set(i,t,n,a)}const M=`
|
|
3
3
|
varying vec2 vUv;
|
|
4
|
+
varying vec4 vScreenPos;
|
|
4
5
|
void main() {
|
|
5
6
|
vUv = uv;
|
|
6
7
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
8
|
+
vScreenPos = gl_Position;
|
|
7
9
|
}
|
|
8
|
-
`,
|
|
10
|
+
`,w=`
|
|
11
|
+
|
|
9
12
|
varying vec2 vUv;
|
|
10
13
|
|
|
11
14
|
uniform vec2 uSize;
|
|
12
|
-
uniform
|
|
15
|
+
uniform vec4 uBorderRadius;
|
|
13
16
|
uniform float uBorderWidth;
|
|
14
|
-
uniform vec3
|
|
17
|
+
uniform vec3 uBgColor;
|
|
15
18
|
uniform vec3 uBorderColor;
|
|
16
19
|
uniform float uOpacity;
|
|
17
20
|
uniform float uBgOpacity;
|
|
21
|
+
uniform float uBorderOpacity;
|
|
22
|
+
|
|
23
|
+
#INJECT_DECLARATIONS
|
|
18
24
|
|
|
19
|
-
// SDF 박스 함수
|
|
20
25
|
float sdRoundedBox(vec2 p, vec2 b, float r) {
|
|
21
26
|
vec2 q = abs(p) - b + r;
|
|
27
|
+
|
|
22
28
|
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
void main() {
|
|
26
32
|
vec2 p = (vUv - 0.5) * uSize;
|
|
27
33
|
vec2 halfSize = uSize * 0.5;
|
|
28
|
-
|
|
29
|
-
float d = sdRoundedBox(p, halfSize, uRadius);
|
|
30
|
-
|
|
31
|
-
float smoothEdge = 1.0;
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
#INJECT_UV_MODIFIER
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (uBorderWidth > 0.01) {
|
|
38
|
-
borderAlpha = (1.0 - smoothstep(0.0, smoothEdge, d)) - fillAlpha;
|
|
39
|
-
}
|
|
37
|
+
// color decision pipeline
|
|
38
|
+
vec4 baseColor = vec4(uBgColor, uBgOpacity);
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
#INJECT_BASE_COLOR
|
|
41
|
+
|
|
42
|
+
// sdf shape pipeline
|
|
43
|
+
vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
|
|
44
|
+
float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
|
|
45
|
+
float d = sdRoundedBox(p, halfSize, r);
|
|
46
|
+
|
|
47
|
+
float aa = 1.0;
|
|
48
|
+
float bgMask = 1.0 - smoothstep(0.0, aa, d);
|
|
49
|
+
|
|
50
|
+
float halfBorder = uBorderWidth * 0.5;
|
|
51
|
+
float borderD = abs(d + halfBorder) - halfBorder;
|
|
52
|
+
float borderAlpha = (1.0 - smoothstep(0.0, aa, borderD)) * uBorderOpacity;
|
|
53
|
+
if (uBorderWidth <= 0.01) {
|
|
54
|
+
borderAlpha = 0.0;
|
|
46
55
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
float
|
|
50
|
-
|
|
56
|
+
|
|
57
|
+
// final blending (border + background)
|
|
58
|
+
float aFront = borderAlpha;
|
|
59
|
+
float aBack = baseColor.a;
|
|
60
|
+
float aOut = aFront + aBack * (1.0 - aFront);
|
|
61
|
+
|
|
62
|
+
float safeAlpha = max(aOut, 0.0001);
|
|
63
|
+
vec3 cOut = (uBorderColor * aFront + baseColor.rgb * aBack * (1.0 - aFront)) / safeAlpha;
|
|
64
|
+
vec4 finalColor = vec4(cOut, aOut);
|
|
65
|
+
|
|
66
|
+
// final color control (Tint, Noise)
|
|
67
|
+
#INJECT_COLOR_MODIFIER
|
|
68
|
+
|
|
69
|
+
float finalOpacity = finalColor.a * bgMask * uOpacity;
|
|
51
70
|
if (finalOpacity < 0.001) discard;
|
|
52
71
|
|
|
53
|
-
gl_FragColor = vec4(
|
|
72
|
+
gl_FragColor = vec4(finalColor.rgb, finalOpacity);
|
|
73
|
+
|
|
74
|
+
#include <colorspace_fragment>
|
|
54
75
|
}
|
|
55
|
-
`;function
|
|
76
|
+
`;function g(e){if(!e||e==="transparent")return{color:new c.Color(16777215),alpha:0};const r=e.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(r){const o=parseInt(r[1],10),i=parseInt(r[2],10),t=parseInt(r[3],10),n=r[4]!==void 0?parseFloat(r[4]):1;return{color:new c.Color(`rgb(${o}, ${i}, ${t})`),alpha:n}}return{color:new c.Color(e),alpha:1}}function O(e,r,o,i=null,t){const n=i!==null,a=n?`
|
|
77
|
+
uniform sampler2D uTexture;
|
|
78
|
+
varying vec4 vScreenPos;
|
|
79
|
+
`:"",l=n?`
|
|
80
|
+
vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
81
|
+
vec2 resultUv = screenUv;
|
|
82
|
+
${(t==null?void 0:t.uvModifier)||""}
|
|
83
|
+
`:"",u=n?`
|
|
84
|
+
baseColor = texture2D(uTexture, resultUv);
|
|
85
|
+
`:"",d=(t==null?void 0:t.colorModifier)||"",x=w.replace("#INJECT_DECLARATIONS",a).replace("#INJECT_UV_MODIFIER",l).replace("#INJECT_BASE_COLOR",u).replace("#INJECT_COLOR_MODIFIER",d),f=g(e.backgroundColor),b=g(e.borderColor),B={uSize:{value:new c.Vector2(r,o)},uBorderRadius:{value:new c.Vector4(0,0,0,0)},uBorderWidth:{value:p(e.borderWidth)},uBgColor:{value:f.color},uBorderColor:{value:b.color},uOpacity:{value:e.opacity??1},uBgOpacity:{value:f.alpha},uBorderOpacity:{value:b.alpha},uTexture:{value:null}};return h(B.uBorderRadius.value,e.borderRadius),n&&(B.uTexture.value=i),new c.ShaderMaterial({uniforms:B,vertexShader:M,fragmentShader:x,transparent:!0,side:c.FrontSide})}function T(e,r,o,i,t){const n=g(r.backgroundColor),a=g(r.borderColor);e.uniforms.uSize.value.set(o,i),h(e.uniforms.uBorderRadius.value,r.borderRadius),e.uniforms.uBorderWidth.value=p(r.borderWidth),e.uniforms.uBgColor.value.copy(n.color),e.uniforms.uBorderColor.value.copy(a.color),e.uniforms.uOpacity.value=r.opacity??1,e.uniforms.uBgOpacity.value=n.alpha,e.uniforms.uBorderOpacity.value=a.alpha,e.uniforms.uTexture&&t!==void 0&&(e.uniforms.uTexture.value=t)}const y={create(e,r,o,i,t,n=2,a=null){if(e==="BOX")return O(r,i,t,a);if(e==="TEXT"){const l=m(o||"",r,i,t,n);return new c.MeshBasicMaterial({map:l,transparent:!0,side:c.FrontSide,color:16777215})}return new c.MeshBasicMaterial({visible:!1})},update(e,r,o,i,t,n,a=2,l){if(r==="BOX")T(e,o,t,n,l);else if(r==="TEXT"){const u=e;u.map&&u.map.dispose();const d=m(i||"",o,t,n,a);u.map=d,u.needsUpdate=!0}}};s.Painter=y,s.createBoxMaterial=O,s.createTextTexture=m,s.updateBoxMaterial=T,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { BoxStyles } from './types';
|
|
2
2
|
import * as THREE from "three";
|
|
3
|
-
export declare function createBoxMaterial(styles: BoxStyles, width: number, height: number
|
|
4
|
-
|
|
3
|
+
export declare function createBoxMaterial(styles: BoxStyles, width: number, height: number, texture?: THREE.Texture | null, hooks?: {
|
|
4
|
+
uvModifier?: string;
|
|
5
|
+
colorModifier?: string;
|
|
6
|
+
}): THREE.ShaderMaterial;
|
|
7
|
+
export declare function updateBoxMaterial(material: THREE.ShaderMaterial, styles: BoxStyles, width: number, height: number, texture?: THREE.Texture | null): void;
|
package/dist/src/Painter.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
2
|
export declare const Painter: {
|
|
3
|
-
create(type: "BOX" | "TEXT", styles: any, content: string, width: number, height: number, quality?: number): THREE.Material;
|
|
4
|
-
update(material: THREE.Material, type: "BOX" | "TEXT", styles: any, content: string, width: number, height: number, quality?: number): void;
|
|
3
|
+
create(type: "BOX" | "TEXT", styles: any, content: string, width: number, height: number, quality?: number, texture?: THREE.Texture | null): THREE.Material;
|
|
4
|
+
update(material: THREE.Material, type: "BOX" | "TEXT", styles: any, content: string, width: number, height: number, quality?: number, texture?: THREE.Texture | null): void;
|
|
5
5
|
};
|
|
File without changes
|
package/package.json
CHANGED
package/src/BoxGenerator.ts
CHANGED
|
@@ -6,60 +6,95 @@ function parsePixelValue(value: string | number): number {
|
|
|
6
6
|
return parseFloat(value) || 0;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
function setBorderRadius(target: THREE.Vector4, radius: string) {
|
|
10
|
+
if (!radius) {
|
|
11
|
+
target.set(0, 0, 0, 0);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const arr = radius.split("/")[0].trim().split(/\s+/);
|
|
15
|
+
const tl = parsePixelValue(arr[0]);
|
|
16
|
+
const tr = parsePixelValue(arr[1] ?? arr[0]);
|
|
17
|
+
const br = parsePixelValue(arr[2] ?? arr[0]);
|
|
18
|
+
const bl = parsePixelValue(arr[3] ?? arr[1] ?? arr[0]);
|
|
19
|
+
|
|
20
|
+
target.set(tl, tr, br, bl);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const vertexShader = /* glsl */ `
|
|
10
24
|
varying vec2 vUv;
|
|
25
|
+
varying vec4 vScreenPos;
|
|
11
26
|
void main() {
|
|
12
27
|
vUv = uv;
|
|
13
28
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
29
|
+
vScreenPos = gl_Position;
|
|
14
30
|
}
|
|
15
31
|
`;
|
|
16
32
|
|
|
17
|
-
const
|
|
33
|
+
const fragmentShaderTemplate = /* glsl */ `
|
|
34
|
+
|
|
18
35
|
varying vec2 vUv;
|
|
19
36
|
|
|
20
37
|
uniform vec2 uSize;
|
|
21
|
-
uniform
|
|
38
|
+
uniform vec4 uBorderRadius;
|
|
22
39
|
uniform float uBorderWidth;
|
|
23
|
-
uniform vec3
|
|
40
|
+
uniform vec3 uBgColor;
|
|
24
41
|
uniform vec3 uBorderColor;
|
|
25
42
|
uniform float uOpacity;
|
|
26
43
|
uniform float uBgOpacity;
|
|
44
|
+
uniform float uBorderOpacity;
|
|
45
|
+
|
|
46
|
+
#INJECT_DECLARATIONS
|
|
27
47
|
|
|
28
|
-
// SDF 박스 함수
|
|
29
48
|
float sdRoundedBox(vec2 p, vec2 b, float r) {
|
|
30
49
|
vec2 q = abs(p) - b + r;
|
|
50
|
+
|
|
31
51
|
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
|
32
52
|
}
|
|
33
53
|
|
|
34
54
|
void main() {
|
|
35
55
|
vec2 p = (vUv - 0.5) * uSize;
|
|
36
56
|
vec2 halfSize = uSize * 0.5;
|
|
37
|
-
|
|
38
|
-
float d = sdRoundedBox(p, halfSize, uRadius);
|
|
39
|
-
|
|
40
|
-
float smoothEdge = 1.0;
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
float borderAlpha = 0.0;
|
|
58
|
+
#INJECT_UV_MODIFIER
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
60
|
+
// color decision pipeline
|
|
61
|
+
vec4 baseColor = vec4(uBgColor, uBgOpacity);
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
#INJECT_BASE_COLOR
|
|
64
|
+
|
|
65
|
+
// sdf shape pipeline
|
|
66
|
+
vec2 xRadii = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
|
|
67
|
+
float r = mix(xRadii.y, xRadii.x, step(0.0, p.y));
|
|
68
|
+
float d = sdRoundedBox(p, halfSize, r);
|
|
69
|
+
|
|
70
|
+
float aa = 1.0;
|
|
71
|
+
float bgMask = 1.0 - smoothstep(0.0, aa, d);
|
|
72
|
+
|
|
73
|
+
float halfBorder = uBorderWidth * 0.5;
|
|
74
|
+
float borderD = abs(d + halfBorder) - halfBorder;
|
|
75
|
+
float borderAlpha = (1.0 - smoothstep(0.0, aa, borderD)) * uBorderOpacity;
|
|
76
|
+
if (uBorderWidth <= 0.01) {
|
|
77
|
+
borderAlpha = 0.0;
|
|
55
78
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
float
|
|
59
|
-
|
|
79
|
+
|
|
80
|
+
// final blending (border + background)
|
|
81
|
+
float aFront = borderAlpha;
|
|
82
|
+
float aBack = baseColor.a;
|
|
83
|
+
float aOut = aFront + aBack * (1.0 - aFront);
|
|
84
|
+
|
|
85
|
+
float safeAlpha = max(aOut, 0.0001);
|
|
86
|
+
vec3 cOut = (uBorderColor * aFront + baseColor.rgb * aBack * (1.0 - aFront)) / safeAlpha;
|
|
87
|
+
vec4 finalColor = vec4(cOut, aOut);
|
|
88
|
+
|
|
89
|
+
// final color control (Tint, Noise)
|
|
90
|
+
#INJECT_COLOR_MODIFIER
|
|
91
|
+
|
|
92
|
+
float finalOpacity = finalColor.a * bgMask * uOpacity;
|
|
60
93
|
if (finalOpacity < 0.001) discard;
|
|
61
94
|
|
|
62
|
-
gl_FragColor = vec4(
|
|
95
|
+
gl_FragColor = vec4(finalColor.rgb, finalOpacity);
|
|
96
|
+
|
|
97
|
+
#include <colorspace_fragment>
|
|
63
98
|
}
|
|
64
99
|
`;
|
|
65
100
|
|
|
@@ -88,19 +123,60 @@ export function createBoxMaterial(
|
|
|
88
123
|
styles: BoxStyles,
|
|
89
124
|
width: number,
|
|
90
125
|
height: number,
|
|
126
|
+
texture: THREE.Texture | null = null,
|
|
127
|
+
hooks?: { uvModifier?: string; colorModifier?: string },
|
|
91
128
|
): THREE.ShaderMaterial {
|
|
129
|
+
const hasTexture = texture !== null;
|
|
130
|
+
|
|
131
|
+
const declChunk = hasTexture
|
|
132
|
+
? /* glsl */ `
|
|
133
|
+
uniform sampler2D uTexture;
|
|
134
|
+
varying vec4 vScreenPos;
|
|
135
|
+
`
|
|
136
|
+
: "";
|
|
137
|
+
|
|
138
|
+
const uvChunk = hasTexture
|
|
139
|
+
? /* glsl */ `
|
|
140
|
+
vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
141
|
+
vec2 resultUv = screenUv;
|
|
142
|
+
${hooks?.uvModifier || ""}
|
|
143
|
+
`
|
|
144
|
+
: "";
|
|
145
|
+
|
|
146
|
+
const baseColorChunk = hasTexture
|
|
147
|
+
? /* glsl */ `
|
|
148
|
+
baseColor = texture2D(uTexture, resultUv);
|
|
149
|
+
`
|
|
150
|
+
: "";
|
|
151
|
+
|
|
152
|
+
const colorModChunk = hooks?.colorModifier || "";
|
|
153
|
+
|
|
154
|
+
const fragmentShader = fragmentShaderTemplate
|
|
155
|
+
.replace("#INJECT_DECLARATIONS", declChunk)
|
|
156
|
+
.replace("#INJECT_UV_MODIFIER", uvChunk)
|
|
157
|
+
.replace("#INJECT_BASE_COLOR", baseColorChunk)
|
|
158
|
+
.replace("#INJECT_COLOR_MODIFIER", colorModChunk);
|
|
159
|
+
|
|
160
|
+
// uniform setting
|
|
92
161
|
const parsedBg = parseColor(styles.backgroundColor);
|
|
93
162
|
const parsedBorder = parseColor(styles.borderColor);
|
|
94
|
-
|
|
95
163
|
const uniforms = {
|
|
96
164
|
uSize: { value: new THREE.Vector2(width, height) },
|
|
97
|
-
|
|
165
|
+
uBorderRadius: { value: new THREE.Vector4(0, 0, 0, 0) },
|
|
98
166
|
uBorderWidth: { value: parsePixelValue(styles.borderWidth) },
|
|
99
|
-
|
|
167
|
+
uBgColor: { value: parsedBg.color },
|
|
100
168
|
uBorderColor: { value: parsedBorder.color },
|
|
101
169
|
uOpacity: { value: styles.opacity ?? 1.0 },
|
|
102
170
|
uBgOpacity: { value: parsedBg.alpha },
|
|
171
|
+
uBorderOpacity: { value: parsedBorder.alpha },
|
|
172
|
+
uTexture: { value: null as THREE.Texture | null },
|
|
103
173
|
};
|
|
174
|
+
// border radius value initialize
|
|
175
|
+
setBorderRadius(uniforms.uBorderRadius.value, styles.borderRadius);
|
|
176
|
+
|
|
177
|
+
if (hasTexture) {
|
|
178
|
+
uniforms.uTexture.value = texture;
|
|
179
|
+
}
|
|
104
180
|
|
|
105
181
|
const material = new THREE.ShaderMaterial({
|
|
106
182
|
uniforms: uniforms,
|
|
@@ -118,18 +194,24 @@ export function updateBoxMaterial(
|
|
|
118
194
|
styles: BoxStyles,
|
|
119
195
|
width: number,
|
|
120
196
|
height: number,
|
|
197
|
+
texture?: THREE.Texture | null
|
|
121
198
|
) {
|
|
122
199
|
const parsedBg = parseColor(styles.backgroundColor);
|
|
123
200
|
const parsedBorder = parseColor(styles.borderColor);
|
|
124
201
|
|
|
125
202
|
material.uniforms.uSize.value.set(width, height);
|
|
126
203
|
|
|
127
|
-
material.uniforms.
|
|
204
|
+
// material.uniforms.uBorderRadius.value = parsePixelValue(styles.borderRadius);
|
|
205
|
+
setBorderRadius(material.uniforms.uBorderRadius.value, styles.borderRadius);
|
|
128
206
|
material.uniforms.uBorderWidth.value = parsePixelValue(styles.borderWidth);
|
|
129
207
|
|
|
130
|
-
material.uniforms.
|
|
208
|
+
material.uniforms.uBgColor.value.copy(parsedBg.color);
|
|
131
209
|
material.uniforms.uBorderColor.value.copy(parsedBorder.color);
|
|
132
210
|
|
|
133
211
|
material.uniforms.uOpacity.value = styles.opacity ?? 1.0;
|
|
134
212
|
material.uniforms.uBgOpacity.value = parsedBg.alpha;
|
|
213
|
+
material.uniforms.uBorderOpacity.value = parsedBorder.alpha;
|
|
214
|
+
if (material.uniforms.uTexture && texture !== undefined) {
|
|
215
|
+
material.uniforms.uTexture.value = texture;
|
|
216
|
+
}
|
|
135
217
|
}
|
package/src/Painter.ts
CHANGED
|
@@ -11,9 +11,10 @@ export const Painter = {
|
|
|
11
11
|
width: number,
|
|
12
12
|
height: number,
|
|
13
13
|
quality: number = 2,
|
|
14
|
+
texture: THREE.Texture | null = null,
|
|
14
15
|
): THREE.Material {
|
|
15
16
|
if (type === "BOX") {
|
|
16
|
-
return createBoxMaterial(styles as BoxStyles, width, height);
|
|
17
|
+
return createBoxMaterial(styles as BoxStyles, width, height, texture);
|
|
17
18
|
} else if (type === "TEXT") {
|
|
18
19
|
const texture = createTextTexture(
|
|
19
20
|
content || "",
|
|
@@ -42,6 +43,7 @@ export const Painter = {
|
|
|
42
43
|
width: number,
|
|
43
44
|
height: number,
|
|
44
45
|
quality: number = 2,
|
|
46
|
+
texture?: THREE.Texture | null
|
|
45
47
|
) {
|
|
46
48
|
if (type === "BOX") {
|
|
47
49
|
updateBoxMaterial(
|
|
@@ -49,6 +51,7 @@ export const Painter = {
|
|
|
49
51
|
styles as BoxStyles,
|
|
50
52
|
width,
|
|
51
53
|
height,
|
|
54
|
+
texture
|
|
52
55
|
);
|
|
53
56
|
} else if (type === "TEXT") {
|
|
54
57
|
const basicMat = material as THREE.MeshBasicMaterial;
|
|
File without changes
|