react-3d-dice 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BosDev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # react-3d-dice
2
+
3
+ 3D dice renderer for React using Three.js. Renders D4, D6, D8, D10, D12, and D20 with canvas-textured face labels, settle animations, and optional D6 dot pips.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install react-3d-dice three
9
+ ```
10
+
11
+ > **Peer dependencies:** `react >= 17`, `react-dom >= 17`, `three >= 0.150`
12
+
13
+ ## Quick Start
14
+
15
+ ```jsx
16
+ import { useState } from 'react';
17
+ import Dice3D from 'react-3d-dice';
18
+
19
+ function App() {
20
+ const [results, setResults] = useState([]);
21
+ const [isRolling, setIsRolling] = useState(false);
22
+ const [trigger, setTrigger] = useState(0);
23
+
24
+ const roll = () => {
25
+ setIsRolling(true);
26
+ const newResults = Array.from({ length: 2 }, () =>
27
+ Math.floor(Math.random() * 6) + 1
28
+ );
29
+ setResults(newResults);
30
+ setTrigger(t => t + 1);
31
+ setTimeout(() => setIsRolling(false), 800);
32
+ };
33
+
34
+ return (
35
+ <div>
36
+ <Dice3D
37
+ sides={6}
38
+ color={0x3b82f6}
39
+ results={results}
40
+ isRolling={isRolling}
41
+ rollTrigger={trigger}
42
+ />
43
+ <button onClick={roll}>Roll 2D6</button>
44
+ </div>
45
+ );
46
+ }
47
+ ```
48
+
49
+ ## Props
50
+
51
+ | Prop | Type | Default | Description |
52
+ |------|------|---------|-------------|
53
+ | `sides` | `number` | (required) | Number of sides: 4, 6, 8, 10, 12, 20 |
54
+ | `color` | `number \| string` | `0x3b82f6` | Die color as hex number or CSS hex string |
55
+ | `results` | `number[]` | `[]` | Roll results (one per die rendered) |
56
+ | `isRolling` | `boolean` | `false` | Whether dice are currently rolling |
57
+ | `animationMode` | `'full' \| 'quick' \| 'none'` | `'full'` | Animation style |
58
+ | `rollTrigger` | `number` | `0` | Increment to trigger a new roll |
59
+ | `d6Style` | `'numbers' \| 'dots'` | `'numbers'` | D6 label style (numbers or dot pips) |
60
+ | `height` | `number` | auto | Container height in px |
61
+ | `className` | `string` | `undefined` | CSS class for the container |
62
+ | `style` | `object` | `undefined` | Inline styles merged onto container |
63
+ | `emptyText` | `string` | `'Press Roll...'` | Text when no results |
64
+
65
+ ## Supported Dice
66
+
67
+ | Die | Geometry | Labels |
68
+ |-----|----------|--------|
69
+ | D4 | Tetrahedron | Face numbers |
70
+ | D6 | Cube | Numbers or dot pips |
71
+ | D8 | Octahedron | Face numbers |
72
+ | D10 | Pentagonal trapezohedron | Face numbers (odds top, evens bottom) |
73
+ | D12 | Dodecahedron | Face numbers |
74
+ | D20 | Icosahedron | Face numbers |
75
+ | Other | Icosahedron (unlabeled) | Overlay numbers |
76
+
77
+ Opposite faces always sum to N+1 (e.g. D6: 7, D20: 21).
78
+
79
+ ## Engine API
80
+
81
+ For advanced usage (custom Three.js scenes), import engine utilities:
82
+
83
+ ```js
84
+ import { buildDieMesh, settleQuat, createGeometry } from 'react-3d-dice';
85
+
86
+ // Build a die mesh for your own scene
87
+ const mesh = buildDieMesh(20, 0xef4444, 'numbers');
88
+ scene.add(mesh);
89
+
90
+ // Get settle quaternion for a result
91
+ const quat = settleQuat(mesh, 17, 20);
92
+ mesh.quaternion.copy(quat);
93
+ ```
94
+
95
+ ### Engine Exports
96
+
97
+ | Export | Description |
98
+ |--------|-------------|
99
+ | `buildDieMesh(sides, color, d6Style)` | Create a complete die mesh with edges and labels |
100
+ | `settleQuat(mesh, result, sides)` | Get quaternion to show a specific face |
101
+ | `createGeometry(sides)` | Create raw Three.js geometry |
102
+ | `createD10Geometry(radius)` | Pentagonal trapezohedron geometry |
103
+ | `computeFaces(geometry, numFaces)` | Extract face data (centroid, normal, vertices) |
104
+ | `computeFaceNumbers(faces, sides)` | Assign numbers with opposite-face pairing |
105
+ | `faceSettleQuat(face, sides)` | Settle quaternion for a single face |
106
+ | `parseColor(color)` | Convert hex string/number to Three.js color int |
107
+ | `clearTextureCache()` | Dispose cached canvas textures |
108
+ | `getNumTexture(num)` / `getDotTexture(num)` | Get cached canvas textures |
109
+
110
+ ## How It Works
111
+
112
+ - Uses `OrthographicCamera` for zero perspective distortion
113
+ - Face labels are canvas textures on `PlaneGeometry` meshes positioned at face centroids
114
+ - Normal-direction clustering detects geometric faces on non-indexed polyhedra
115
+ - Vertex-snapping orients numbers to align with face polygon vertices (D4/D8/D12/D20)
116
+ - D10 uses custom pentagonal trapezohedron geometry with pole-based text orientation
117
+ - Settle animation uses quaternion slerp with cubic ease-out
118
+
119
+ ## License
120
+
121
+ MIT
@@ -0,0 +1,7 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const _=require("react/jsx-runtime"),j=require("react"),xe=require("three");function ye(e){const u=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(u,n,r.get?r:{enumerable:!0,get:()=>e[n]})}}return u.default=e,Object.freeze(u)}const o=ye(xe),Q=new Set([4,6,8,10,12,20]),te={4:.95,6:1.45,8:.82,10:.68,12:.62,20:.56},ne=.6,oe=[1,6,2,5,3,4],K={"bg-purple-500":11032055,"bg-blue-500":3900150,"bg-green-500":2278750,"bg-yellow-500":15381256,"bg-orange-500":16347926,"bg-red-500":15680580,"bg-pink-500":15485081,"bg-gray-500":7041664};function re(e){if(typeof e=="number")return e;if(typeof e=="string"){if(e.startsWith("#"))return parseInt(e.slice(1),16);if(K[e])return K[e]}return 7041664}const L=new Map;function Me(){L.forEach(e=>e.dispose()),L.clear()}function se(e){if(L.has(e))return L.get(e);const u=256,n=document.createElement("canvas");n.width=u,n.height=u;const r=n.getContext("2d");r.clearRect(0,0,u,u);const h=String(e),a=h.length>1?u*.62:u*.82;r.font=`bold ${a}px Arial, sans-serif`,r.textAlign="center",r.textBaseline="middle",r.fillStyle="rgba(0,0,0,0.35)",r.fillText(h,u/2+2,u/2+2),r.fillStyle="#ffffff",r.fillText(h,u/2,u/2);const f=new o.CanvasTexture(n);return f.needsUpdate=!0,L.set(e,f),f}const ee={1:[[.5,.5]],2:[[.22,.78],[.78,.22]],3:[[.22,.78],[.5,.5],[.78,.22]],4:[[.22,.22],[.78,.22],[.22,.78],[.78,.78]],5:[[.22,.22],[.78,.22],[.5,.5],[.22,.78],[.78,.78]],6:[[.22,.18],[.22,.5],[.22,.82],[.78,.18],[.78,.5],[.78,.82]]};function ae(e){const u=`dot-${e}`;if(L.has(u))return L.get(u);const n=256,r=document.createElement("canvas");r.width=n,r.height=n;const h=r.getContext("2d");h.clearRect(0,0,n,n);const a=ee[e]||ee[1],f=e<=2?n*.13:n*.105;a.forEach(([l,s])=>{h.beginPath(),h.arc(l*n+1,s*n+1,f,0,Math.PI*2),h.fillStyle="rgba(0,0,0,0.3)",h.fill(),h.beginPath(),h.arc(l*n,s*n,f,0,Math.PI*2),h.fillStyle="#ffffff",h.fill()});const t=new o.CanvasTexture(r);return t.needsUpdate=!0,L.set(u,t),t}function ce(e){const u=e,n=u*(1-Math.cos(Math.PI/5))/(1+Math.cos(Math.PI/5)),r=e*.85,h=[0,u,0],a=[0,-u,0],f=[],t=[];for(let d=0;d<5;d++){const g=d*2*Math.PI/5,w=g+Math.PI/5;f.push([r*Math.cos(g),n,r*Math.sin(g)]),t.push([r*Math.cos(w),-n,r*Math.sin(w)])}const l=[];function s(d){l.push(d[0],d[1],d[2])}for(let d=0;d<5;d++){const g=(d+1)%5;s(h),s(t[d]),s(f[d]),s(h),s(f[g]),s(t[d]),s(a),s(t[d]),s(f[g]),s(a),s(f[g]),s(t[g])}const m=new o.BufferGeometry;return m.setAttribute("position",new o.Float32BufferAttribute(l,3)),m.computeVertexNormals(),m}function ie(e){switch(e){case 4:return new o.TetrahedronGeometry(1.2,0);case 6:return new o.BoxGeometry(1.7,1.7,1.7);case 8:return new o.OctahedronGeometry(1.2,0);case 10:return ce(1.2);case 12:return new o.DodecahedronGeometry(1.2,0);case 20:return new o.IcosahedronGeometry(1.2,0);default:return new o.IcosahedronGeometry(1.14,0)}}function le(e){return e===4?4:e===6?6:e===8?8:e===10?10:e===12?12:20}function ue(e,u){const n=e.getAttribute("position"),r=e.index;if(r){const f=r.count/3,t=Math.round(f/u),l=[];for(let s=0;s<u;s++){const m=new o.Vector3,d=new o.Vector3,g=[],w=new Set;let x=0;for(let y=s*t;y<(s+1)*t;y++){const A=y*3,z=r.getX(A),B=r.getX(A+1),P=r.getX(A+2),T=new o.Vector3().fromBufferAttribute(n,z),O=new o.Vector3().fromBufferAttribute(n,B),H=new o.Vector3().fromBufferAttribute(n,P);m.add(T).add(O).add(H),x+=3,y===s*t&&d.crossVectors(new o.Vector3().subVectors(O,T),new o.Vector3().subVectors(H,T)).normalize(),w.has(z)||(w.add(z),g.push(T)),w.has(B)||(w.add(B),g.push(O)),w.has(P)||(w.add(P),g.push(H))}m.divideScalar(x),d.dot(m)<0&&d.negate(),l.push({centroid:m.clone(),normal:d.clone(),verts:g})}return l}const h=n.count/3,a=[];for(let f=0;f<h;f++){const t=f*3,l=new o.Vector3().fromBufferAttribute(n,t),s=new o.Vector3().fromBufferAttribute(n,t+1),m=new o.Vector3().fromBufferAttribute(n,t+2),d=new o.Vector3().crossVectors(new o.Vector3().subVectors(s,l),new o.Vector3().subVectors(m,l)).normalize(),g=new o.Vector3().add(l).add(s).add(m).divideScalar(3);d.dot(g)<0&&d.negate();let w=!1;for(const x of a)if(x.normal.dot(d)>.999){x.verts.push(l,s,m),w=!0;break}w||a.push({normal:d.clone(),verts:[l,s,m]})}return a.sort((f,t)=>{const l=f.normal,s=t.normal;return Math.abs(l.x-s.x)>.001?l.x-s.x:Math.abs(l.y-s.y)>.001?l.y-s.y:l.z-s.z}),a.map(f=>{const t=new o.Vector3;f.verts.forEach(s=>t.add(s)),t.divideScalar(f.verts.length);const l=[];for(const s of f.verts){let m=!1;for(const d of l)if(s.distanceTo(d)<.001){m=!0;break}m||l.push(s.clone())}return{centroid:t.clone(),normal:f.normal.clone(),verts:l}})}function fe(e,u){if(u===6)return oe;if(u===4)return[1,2,3,4];const n=e.length,r=new Array(n).fill(0),h=u+1,a=[],f=new Set;for(let t=0;t<n;t++)if(!f.has(t)){for(let l=t+1;l<n;l++)if(!f.has(l)&&e[t].normal.dot(e[l].normal)<-.99){a.push([t,l]),f.add(t),f.add(l);break}f.has(t)||(a.push([t,-1]),f.add(t))}if(u===10){a.forEach(t=>{t[1]>=0&&e[t[0]].centroid.y<e[t[1]].centroid.y&&([t[0],t[1]]=[t[1],t[0]])}),a.sort((t,l)=>{const s=Math.atan2(e[t[0]].centroid.z,e[t[0]].centroid.x),m=Math.atan2(e[l[0]].centroid.z,e[l[0]].centroid.x);return s-m});for(let t=0;t<a.length;t++)r[a[t][0]]=t*2+1,a[t][1]>=0&&(r[a[t][1]]=h-(t*2+1));return r}for(let t=0;t<a.length;t++)r[a[t][0]]=t+1,a[t][1]>=0&&(r[a[t][1]]=h-(t+1));return r}function J(e,u){const n=e.normal.clone().normalize();let r=new o.Vector3(0,1,0);Math.abs(n.dot(r))>.99&&(r=new o.Vector3(0,0,1));const h=r.clone().addScaledVector(n,-r.dot(n)).normalize();let a;if(u===10){const s=e.centroid.y>0?new o.Vector3(0,1.2,0):new o.Vector3(0,-1.2,0),m=new o.Vector3().subVectors(s,e.centroid);m.addScaledVector(n,-m.dot(n)).normalize(),a=m.negate()}else if(u!==6&&e.verts&&e.verts.length>=3){let l=-1/0;a=h;for(const s of e.verts){const m=new o.Vector3().subVectors(s,e.centroid).normalize(),d=m.dot(h);d>l&&(l=d,a=m)}}else a=h;const f=new o.Vector3().crossVectors(a,n).normalize(),t=new o.Matrix4().makeBasis(f,a,n);return new o.Quaternion().setFromRotationMatrix(t).conjugate()}function de(e,u,n){const r=ie(e),h=new o.MeshPhongMaterial({color:u,shininess:80,specular:3355443,flatShading:e!==6}),a=new o.Mesh(r,h),f=new o.EdgesGeometry(r),t=new o.LineBasicMaterial({color:16777215,transparent:!0,opacity:.2});if(a.add(new o.LineSegments(f,t)),Q.has(e)){const l=le(e),s=ue(r,l);a.userData.faces=s;const m=te[e]||.3,d=e===6&&n==="dots",g=fe(s,e);a.userData.faceNumbers=g;const w={};g.forEach((x,y)=>{w[x]=y}),a.userData.numberToFace=w;for(let x=0;x<e;x++){const y=s[x];if(!y)continue;const A=g[x],z=d?ae(A):se(A),B=new o.PlaneGeometry(m,m),P=new o.MeshBasicMaterial({map:z,transparent:!0,depthWrite:!1,side:o.DoubleSide}),T=new o.Mesh(B,P);T.position.copy(y.centroid).addScaledVector(y.normal,.02);const O=J(y,e);T.quaternion.copy(O.conjugate()),a.add(T)}}return a}function Z(e,u,n){const r=e.userData.faces,h=e.userData.numberToFace;if(!r||!h)return null;const a=h[u];return a==null||a<0||a>=r.length?null:J(r[a],n)}const he=({sides:e,color:u=3900150,results:n=[],isRolling:r=!1,animationMode:h="full",rollTrigger:a=0,d6Style:f="numbers",height:t,className:l,style:s,emptyText:m="Press Roll to see 3D dice"})=>{const d=j.useRef(null),g=j.useRef({scene:null,camera:null,renderer:null,meshes:[],gridPos:[],animId:null,phase:"idle",settleStart:0,settleData:[],frustumHalf:2.5}),w=j.useRef(r),x=j.useRef(h),y=j.useRef(Q.has(e));w.current=r,x.current=h,y.current=Q.has(e);const[A,z]=j.useState([]),B=re(u),P=Q.has(e);j.useEffect(()=>{const c=d.current;if(!c)return;const i=g.current,M=new o.Scene,N=c.clientWidth,I=Math.max(c.clientHeight,1),k=N/I,G=2.5,V=new o.OrthographicCamera(-G*k,G*k,G,-G,.1,100);V.position.set(0,0,10);const v=new o.WebGLRenderer({antialias:!0,alpha:!0});v.setPixelRatio(Math.min(window.devicePixelRatio,2)),v.setSize(c.clientWidth,c.clientHeight),v.setClearColor(0,0),c.appendChild(v.domElement),M.add(new o.AmbientLight(16777215,.5));const W=new o.DirectionalLight(16777215,1);W.position.set(5,8,5),M.add(W);const U=new o.DirectionalLight(16777215,.3);U.position.set(-3,-2,4),M.add(U),i.scene=M,i.camera=V,i.renderer=v;function X(){i.gridPos.length!==0&&(V.updateProjectionMatrix(),z(i.gridPos.map(p=>{const b=new o.Vector3(p.x,p.y,0).project(V);return{left:((b.x*.5+.5)*100).toFixed(2)+"%",top:((-b.y*.5+.5)*100).toFixed(2)+"%"}})))}i.updateOverlay=X;const F=new ResizeObserver(()=>{const p=c.clientWidth,b=c.clientHeight;if(p===0||b===0)return;const C=p/b,R=i.frustumHalf;V.left=-R*C,V.right=R*C,V.top=R,V.bottom=-R,V.updateProjectionMatrix(),v.setSize(p,b),X()});F.observe(c);const S=p=>{i.animId=requestAnimationFrame(S);const b=p/1e3,C=i.meshes,R=x.current,$=y.current;if(i.phase==="spinning"&&R!=="none"){const D=R==="full"?10:15;C.forEach((E,q)=>{E.rotation.x+=(D+q*2)*.016,E.rotation.y+=(D*.8+q)*.016,E.rotation.z+=D*.3*.016})}else if(i.phase==="settling"){const D=(p-i.settleStart)/1e3,E=Math.min(D/ne,1),q=1-Math.pow(1-E,3);C.forEach((be,we)=>{const Y=i.settleData[we];Y&&be.quaternion.slerpQuaternions(Y.from,Y.to,q)}),E>=1&&(i.phase="idle")}else i.phase==="idle"&&C.length>0&&C.forEach((D,E)=>{if($&&i.settleData[E]){const q=new o.Quaternion().setFromEuler(new o.Euler(Math.sin(b*.8+E)*.03,Math.sin(b*.6+E*.5)*.05,0));D.quaternion.copy(i.settleData[E].to).multiply(q)}else D.rotation.x+=.004,D.rotation.y+=.006;i.gridPos[E]&&(D.position.y=i.gridPos[E].y+Math.sin(b*1.2+E*.7)*.06)});v.render(M,V)};return i.animId=requestAnimationFrame(S),()=>{F.disconnect(),i.animId&&cancelAnimationFrame(i.animId),i.meshes.forEach(p=>{M.remove(p),p.traverse(b=>{b.geometry&&b.geometry.dispose(),b.material&&b.material.dispose()})}),v.dispose(),v.domElement.parentNode===c&&c.removeChild(v.domElement),i.scene=null,i.camera=null,i.renderer=null,i.meshes=[],i.gridPos=[],i.animId=null}},[]),j.useEffect(()=>{const c=g.current;if(!c.scene)return;c.meshes.forEach(S=>{c.scene.remove(S),S.traverse(p=>{p.geometry&&p.geometry.dispose(),p.material&&p.material.dispose()})}),c.meshes=[],c.gridPos=[],c.settleData=[],c.phase="idle";const i=n.length;if(i===0){z([]);return}const M=Math.min(i,5),N=Math.ceil(i/M),I=2.8;for(let S=0;S<i;S++){const p=de(e,B,f),b=Math.floor(S/M),C=S-b*M,$=(-((b===N-1?i-b*M:M)-1)/2+C)*I,D=((N-1)/2-b)*I;p.position.set($,D,0),p.rotation.set(Math.random()*Math.PI*2,Math.random()*Math.PI*2,Math.random()*Math.PI*2),c.scene.add(p),c.meshes.push(p),c.gridPos.push({x:$,y:D})}const k=d.current,G=Math.min(i,5)*I,V=N*I,v=k?k.clientWidth/Math.max(k.clientHeight,1):1,W=1.5,U=V/2+W,X=(G/2+W)/v,F=Math.max(U,X,2.5);c.frustumHalf=F,c.camera.left=-F*v,c.camera.right=F*v,c.camera.top=F,c.camera.bottom=-F,c.camera.updateProjectionMatrix(),c.updateOverlay&&c.updateOverlay(),!w.current&&P&&(c.phase="settling",c.settleStart=performance.now(),c.settleData=c.meshes.map((S,p)=>({from:S.quaternion.clone(),to:Z(S,n[p],e)||new o.Quaternion})))},[n.length,e,B,a,P,f]),j.useEffect(()=>{const c=g.current;r?(c.phase="spinning",c.settleData=[]):c.phase==="spinning"&&n.length>0&&(c.phase="settling",c.settleStart=performance.now(),c.settleData=c.meshes.map((i,M)=>{const N=i.quaternion.clone();let I=null;return P&&i.userData.faces&&(I=Z(i,n[M],e)),I||(I=new o.Quaternion().setFromEuler(new o.Euler(Math.random()*.3,Math.random()*.3,0))),{from:N,to:I}}))},[r,n,P,e]);const T=Math.ceil(n.length/5)||1,O=Math.max(200,T*130+50),H=t??O,me=!P&&!r&&n.length>0,pe=n.length>10?"0.875rem":n.length>5?"1.1rem":"1.5rem",ge={position:"relative",overflow:"hidden",borderRadius:"0.75rem",height:typeof H=="number"?H+"px":H,...s};return _.jsxs("div",{className:l,style:ge,children:[_.jsx("div",{ref:d,style:{width:"100%",height:"100%"}}),me&&A.length===n.length&&n.map((c,i)=>_.jsx("div",{style:{position:"absolute",pointerEvents:"none",left:A[i].left,top:A[i].top,transform:"translate(-50%, -50%)",zIndex:10,animation:"rdice3d-numIn 0.3s ease-out"},children:_.jsx("span",{style:{fontWeight:"bold",color:"white",fontVariantNumeric:"tabular-nums",fontSize:pe,textShadow:"0 2px 8px rgba(0,0,0,0.9), 0 0 4px rgba(0,0,0,0.6)"},children:c})},`${a}-${i}`)),n.length===0&&!r&&m&&_.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",pointerEvents:"none"},children:_.jsx("p",{style:{fontSize:"1.125rem",fontWeight:600,color:"#9ca3af",margin:0},children:m})}),_.jsx("style",{children:`
2
+ @keyframes rdice3d-numIn {
3
+ from { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
4
+ to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
5
+ }
6
+ `})]})};exports.D6_NUMBERS=oe;exports.Dice3D=he;exports.FACE_LABELED=Q;exports.LABEL_SIZE=te;exports.SETTLE_SECS=ne;exports.buildDieMesh=de;exports.clearTextureCache=Me;exports.computeFaceNumbers=fe;exports.computeFaces=ue;exports.createD10Geometry=ce;exports.createGeometry=ie;exports.default=he;exports.faceSettleQuat=J;exports.geomFaceCount=le;exports.getDotTexture=ae;exports.getNumTexture=se;exports.parseColor=re;exports.settleQuat=Z;
7
+ //# sourceMappingURL=react-3d-dice.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-3d-dice.cjs.js","sources":["../src/diceEngine.js","../src/Dice3D.jsx"],"sourcesContent":["import * as THREE from 'three';\r\n\r\n// --- Constants ---\r\n\r\n/** Dice with exact face count match get canvas-textured face labels */\r\nexport const FACE_LABELED = new Set([4, 6, 8, 10, 12, 20]);\r\n\r\n/** Label plane size per die type (world-space units) */\r\nexport const LABEL_SIZE = { 4: 0.95, 6: 1.45, 8: 0.82, 10: 0.68, 12: 0.62, 20: 0.56 };\r\n\r\n/** Settle animation duration in seconds */\r\nexport const SETTLE_SECS = 0.6;\r\n\r\n/** D6 standard: opposite faces sum to 7. BoxGeometry face order: +x,-x,+y,-y,+z,-z */\r\nexport const D6_NUMBERS = [1, 6, 2, 5, 3, 4];\r\n\r\n// --- Color helpers ---\r\n\r\n/**\r\n * Parse a color value into a Three.js-compatible hex integer.\r\n * Accepts: hex number (0x3b82f6), CSS hex string ('#3b82f6'), or Tailwind class (bg-blue-500).\r\n */\r\nconst TAILWIND_MAP = {\r\n 'bg-purple-500': 0xa855f7,\r\n 'bg-blue-500': 0x3b82f6,\r\n 'bg-green-500': 0x22c55e,\r\n 'bg-yellow-500': 0xeab308,\r\n 'bg-orange-500': 0xf97316,\r\n 'bg-red-500': 0xef4444,\r\n 'bg-pink-500': 0xec4899,\r\n 'bg-gray-500': 0x6b7280,\r\n};\r\n\r\nexport function parseColor(color) {\r\n if (typeof color === 'number') return color;\r\n if (typeof color === 'string') {\r\n if (color.startsWith('#')) return parseInt(color.slice(1), 16);\r\n if (TAILWIND_MAP[color]) return TAILWIND_MAP[color];\r\n }\r\n return 0x6b7280;\r\n}\r\n\r\n// --- Texture cache ---\r\n\r\nconst texCache = new Map();\r\n\r\n/** Clear the texture cache (useful for memory cleanup) */\r\nexport function clearTextureCache() {\r\n texCache.forEach(tex => tex.dispose());\r\n texCache.clear();\r\n}\r\n\r\n/** Get or create a canvas texture with a number label */\r\nexport function getNumTexture(num) {\r\n if (texCache.has(num)) return texCache.get(num);\r\n const sz = 256;\r\n const c = document.createElement('canvas');\r\n c.width = sz;\r\n c.height = sz;\r\n const ctx = c.getContext('2d');\r\n ctx.clearRect(0, 0, sz, sz);\r\n const text = String(num);\r\n const fs = text.length > 1 ? sz * 0.62 : sz * 0.82;\r\n ctx.font = `bold ${fs}px Arial, sans-serif`;\r\n ctx.textAlign = 'center';\r\n ctx.textBaseline = 'middle';\r\n // Shadow for depth\r\n ctx.fillStyle = 'rgba(0,0,0,0.35)';\r\n ctx.fillText(text, sz / 2 + 2, sz / 2 + 2);\r\n // White number\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fillText(text, sz / 2, sz / 2);\r\n const tex = new THREE.CanvasTexture(c);\r\n tex.needsUpdate = true;\r\n texCache.set(num, tex);\r\n return tex;\r\n}\r\n\r\n// D6 dot/pip patterns (traditional die face layouts)\r\nconst DOT_POSITIONS = {\r\n 1: [[0.5, 0.5]],\r\n 2: [[0.22, 0.78], [0.78, 0.22]],\r\n 3: [[0.22, 0.78], [0.5, 0.5], [0.78, 0.22]],\r\n 4: [[0.22, 0.22], [0.78, 0.22], [0.22, 0.78], [0.78, 0.78]],\r\n 5: [[0.22, 0.22], [0.78, 0.22], [0.5, 0.5], [0.22, 0.78], [0.78, 0.78]],\r\n 6: [[0.22, 0.18], [0.22, 0.5], [0.22, 0.82], [0.78, 0.18], [0.78, 0.5], [0.78, 0.82]],\r\n};\r\n\r\n/** Get or create a canvas texture with dot pips (D6 traditional style) */\r\nexport function getDotTexture(num) {\r\n const key = `dot-${num}`;\r\n if (texCache.has(key)) return texCache.get(key);\r\n const sz = 256;\r\n const c = document.createElement('canvas');\r\n c.width = sz;\r\n c.height = sz;\r\n const ctx = c.getContext('2d');\r\n ctx.clearRect(0, 0, sz, sz);\r\n const dots = DOT_POSITIONS[num] || DOT_POSITIONS[1];\r\n const r = num <= 2 ? sz * 0.13 : sz * 0.105;\r\n dots.forEach(([px, py]) => {\r\n // Shadow\r\n ctx.beginPath();\r\n ctx.arc(px * sz + 1, py * sz + 1, r, 0, Math.PI * 2);\r\n ctx.fillStyle = 'rgba(0,0,0,0.3)';\r\n ctx.fill();\r\n // White dot\r\n ctx.beginPath();\r\n ctx.arc(px * sz, py * sz, r, 0, Math.PI * 2);\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fill();\r\n });\r\n const tex = new THREE.CanvasTexture(c);\r\n tex.needsUpdate = true;\r\n texCache.set(key, tex);\r\n return tex;\r\n}\r\n\r\n// --- Geometry helpers ---\r\n\r\n/**\r\n * Pentagonal trapezohedron: the standard D10 shape.\r\n * 12 vertices (2 poles + 5 upper ring + 5 lower ring), 10 kite-shaped faces.\r\n * Each kite is split into 2 coplanar triangles for BufferGeometry.\r\n * Planarity condition: h/H = (1 - cos36) / (1 + cos36)\r\n */\r\nexport function createD10Geometry(radius) {\r\n const H = radius;\r\n const h = H * (1 - Math.cos(Math.PI / 5)) / (1 + Math.cos(Math.PI / 5));\r\n const r = radius * 0.85;\r\n\r\n const top = [0, H, 0];\r\n const bot = [0, -H, 0];\r\n const upper = [];\r\n const lower = [];\r\n\r\n for (let k = 0; k < 5; k++) {\r\n const aU = (k * 2 * Math.PI) / 5;\r\n const aL = aU + Math.PI / 5;\r\n upper.push([r * Math.cos(aU), h, r * Math.sin(aU)]);\r\n lower.push([r * Math.cos(aL), -h, r * Math.sin(aL)]);\r\n }\r\n\r\n const verts = [];\r\n function push3(v) { verts.push(v[0], v[1], v[2]); }\r\n\r\n for (let k = 0; k < 5; k++) {\r\n const nk = (k + 1) % 5;\r\n // Top kite: top, upper[k], lower[k], upper[nk]\r\n push3(top); push3(lower[k]); push3(upper[k]);\r\n push3(top); push3(upper[nk]); push3(lower[k]);\r\n // Bottom kite: bot, lower[k], upper[nk], lower[nk]\r\n push3(bot); push3(lower[k]); push3(upper[nk]);\r\n push3(bot); push3(upper[nk]); push3(lower[nk]);\r\n }\r\n\r\n const geo = new THREE.BufferGeometry();\r\n geo.setAttribute('position', new THREE.Float32BufferAttribute(verts, 3));\r\n geo.computeVertexNormals();\r\n return geo;\r\n}\r\n\r\n/** Create geometry for a given number of sides */\r\nexport function createGeometry(sides) {\r\n switch (sides) {\r\n case 4: return new THREE.TetrahedronGeometry(1.2, 0);\r\n case 6: return new THREE.BoxGeometry(1.7, 1.7, 1.7);\r\n case 8: return new THREE.OctahedronGeometry(1.2, 0);\r\n case 10: return createD10Geometry(1.2);\r\n case 12: return new THREE.DodecahedronGeometry(1.2, 0);\r\n case 20: return new THREE.IcosahedronGeometry(1.2, 0);\r\n default: return new THREE.IcosahedronGeometry(1.14, 0);\r\n }\r\n}\r\n\r\n/** How many geometric faces for each geometry type */\r\nexport function geomFaceCount(sides) {\r\n if (sides === 4) return 4;\r\n if (sides === 6) return 6;\r\n if (sides === 8) return 8;\r\n if (sides === 10) return 10;\r\n if (sides === 12) return 12;\r\n if (sides === 20) return 20;\r\n return 20;\r\n}\r\n\r\n/**\r\n * Compute centroid + outward normal + unique vertices per logical face.\r\n * For indexed geometry (D6 BoxGeometry) uses sequential grouping.\r\n * For non-indexed polyhedra, clusters triangles by normal direction since\r\n * Three.js does NOT group triangles sequentially by polygon face.\r\n */\r\nexport function computeFaces(geometry, numFaces) {\r\n const pos = geometry.getAttribute('position');\r\n const idx = geometry.index;\r\n\r\n // --- Indexed geometry (BoxGeometry / D6): sequential grouping ---\r\n if (idx) {\r\n const totalTris = idx.count / 3;\r\n const tpf = Math.round(totalTris / numFaces);\r\n const faces = [];\r\n for (let f = 0; f < numFaces; f++) {\r\n const centroid = new THREE.Vector3();\r\n const normal = new THREE.Vector3();\r\n const faceVerts = [];\r\n const seenIdx = new Set();\r\n let vertCount = 0;\r\n for (let t = f * tpf; t < (f + 1) * tpf; t++) {\r\n const base = t * 3;\r\n const ia = idx.getX(base), ib = idx.getX(base + 1), ic = idx.getX(base + 2);\r\n const a = new THREE.Vector3().fromBufferAttribute(pos, ia);\r\n const b = new THREE.Vector3().fromBufferAttribute(pos, ib);\r\n const c = new THREE.Vector3().fromBufferAttribute(pos, ic);\r\n centroid.add(a).add(b).add(c);\r\n vertCount += 3;\r\n if (t === f * tpf) {\r\n normal.crossVectors(\r\n new THREE.Vector3().subVectors(b, a),\r\n new THREE.Vector3().subVectors(c, a)\r\n ).normalize();\r\n }\r\n if (!seenIdx.has(ia)) { seenIdx.add(ia); faceVerts.push(a); }\r\n if (!seenIdx.has(ib)) { seenIdx.add(ib); faceVerts.push(b); }\r\n if (!seenIdx.has(ic)) { seenIdx.add(ic); faceVerts.push(c); }\r\n }\r\n centroid.divideScalar(vertCount);\r\n if (normal.dot(centroid) < 0) normal.negate();\r\n faces.push({ centroid: centroid.clone(), normal: normal.clone(), verts: faceVerts });\r\n }\r\n return faces;\r\n }\r\n\r\n // --- Non-indexed polyhedra: cluster triangles by normal direction ---\r\n const totalTris = pos.count / 3;\r\n const clusters = [];\r\n\r\n for (let t = 0; t < totalTris; t++) {\r\n const vi = t * 3;\r\n const a = new THREE.Vector3().fromBufferAttribute(pos, vi);\r\n const b = new THREE.Vector3().fromBufferAttribute(pos, vi + 1);\r\n const c = new THREE.Vector3().fromBufferAttribute(pos, vi + 2);\r\n const n = new THREE.Vector3().crossVectors(\r\n new THREE.Vector3().subVectors(b, a),\r\n new THREE.Vector3().subVectors(c, a)\r\n ).normalize();\r\n const mid = new THREE.Vector3().add(a).add(b).add(c).divideScalar(3);\r\n if (n.dot(mid) < 0) n.negate();\r\n\r\n let found = false;\r\n for (const cl of clusters) {\r\n if (cl.normal.dot(n) > 0.999) {\r\n cl.verts.push(a, b, c);\r\n found = true;\r\n break;\r\n }\r\n }\r\n if (!found) {\r\n clusters.push({ normal: n.clone(), verts: [a, b, c] });\r\n }\r\n }\r\n\r\n clusters.sort((a, b) => {\r\n const an = a.normal, bn = b.normal;\r\n if (Math.abs(an.x - bn.x) > 0.001) return an.x - bn.x;\r\n if (Math.abs(an.y - bn.y) > 0.001) return an.y - bn.y;\r\n return an.z - bn.z;\r\n });\r\n\r\n return clusters.map(cl => {\r\n const centroid = new THREE.Vector3();\r\n cl.verts.forEach(v => centroid.add(v));\r\n centroid.divideScalar(cl.verts.length);\r\n const unique = [];\r\n for (const v of cl.verts) {\r\n let isDup = false;\r\n for (const u of unique) {\r\n if (v.distanceTo(u) < 0.001) { isDup = true; break; }\r\n }\r\n if (!isDup) unique.push(v.clone());\r\n }\r\n return { centroid: centroid.clone(), normal: cl.normal.clone(), verts: unique };\r\n });\r\n}\r\n\r\n/**\r\n * Assign display numbers to faces so opposite faces sum to sides+1.\r\n * D6 uses hardcoded D6_NUMBERS. D4 has no opposite faces. D10 puts odds at top pole.\r\n */\r\nexport function computeFaceNumbers(faces, sides) {\r\n if (sides === 6) return D6_NUMBERS;\r\n if (sides === 4) return [1, 2, 3, 4];\r\n\r\n const n = faces.length;\r\n const numbers = new Array(n).fill(0);\r\n const target = sides + 1;\r\n\r\n // Find opposite pairs: normals with dot < -0.99\r\n const pairs = [];\r\n const used = new Set();\r\n for (let i = 0; i < n; i++) {\r\n if (used.has(i)) continue;\r\n for (let j = i + 1; j < n; j++) {\r\n if (used.has(j)) continue;\r\n if (faces[i].normal.dot(faces[j].normal) < -0.99) {\r\n pairs.push([i, j]);\r\n used.add(i);\r\n used.add(j);\r\n break;\r\n }\r\n }\r\n if (!used.has(i)) {\r\n pairs.push([i, -1]);\r\n used.add(i);\r\n }\r\n }\r\n\r\n if (sides === 10) {\r\n pairs.forEach(p => {\r\n if (p[1] >= 0 && faces[p[0]].centroid.y < faces[p[1]].centroid.y) {\r\n [p[0], p[1]] = [p[1], p[0]];\r\n }\r\n });\r\n pairs.sort((a, b) => {\r\n const aA = Math.atan2(faces[a[0]].centroid.z, faces[a[0]].centroid.x);\r\n const bA = Math.atan2(faces[b[0]].centroid.z, faces[b[0]].centroid.x);\r\n return aA - bA;\r\n });\r\n for (let k = 0; k < pairs.length; k++) {\r\n numbers[pairs[k][0]] = k * 2 + 1;\r\n if (pairs[k][1] >= 0) numbers[pairs[k][1]] = target - (k * 2 + 1);\r\n }\r\n return numbers;\r\n }\r\n\r\n for (let k = 0; k < pairs.length; k++) {\r\n numbers[pairs[k][0]] = k + 1;\r\n if (pairs[k][1] >= 0) numbers[pairs[k][1]] = target - (k + 1);\r\n }\r\n return numbers;\r\n}\r\n\r\n/**\r\n * Compute the settle quaternion for a face: rotates face normal to +Z, text upright.\r\n * Builds a full rotation matrix (not shortest-arc) so text aligns with face geometry.\r\n * D10: pole tip at bottom. D6: project +Y (edge-aligned). Others: snap to nearest vertex.\r\n */\r\nexport function faceSettleQuat(face, sides) {\r\n const n = face.normal.clone().normalize();\r\n\r\n let ref = new THREE.Vector3(0, 1, 0);\r\n if (Math.abs(n.dot(ref)) > 0.99) ref = new THREE.Vector3(0, 0, 1);\r\n const refUp = ref.clone().addScaledVector(n, -ref.dot(n)).normalize();\r\n\r\n let faceUp;\r\n if (sides === 10) {\r\n const H = 1.2;\r\n const pole = face.centroid.y > 0\r\n ? new THREE.Vector3(0, H, 0)\r\n : new THREE.Vector3(0, -H, 0);\r\n const toPole = new THREE.Vector3().subVectors(pole, face.centroid);\r\n toPole.addScaledVector(n, -toPole.dot(n)).normalize();\r\n faceUp = toPole.negate();\r\n } else if (sides !== 6 && face.verts && face.verts.length >= 3) {\r\n let bestDot = -Infinity;\r\n faceUp = refUp;\r\n for (const v of face.verts) {\r\n const dir = new THREE.Vector3().subVectors(v, face.centroid).normalize();\r\n const d = dir.dot(refUp);\r\n if (d > bestDot) {\r\n bestDot = d;\r\n faceUp = dir;\r\n }\r\n }\r\n } else {\r\n faceUp = refUp;\r\n }\r\n\r\n const xAxis = new THREE.Vector3().crossVectors(faceUp, n).normalize();\r\n const m = new THREE.Matrix4().makeBasis(xAxis, faceUp, n);\r\n return new THREE.Quaternion().setFromRotationMatrix(m).conjugate();\r\n}\r\n\r\n/**\r\n * Build a complete die mesh with edges, face labels, and settle metadata.\r\n * Returns a THREE.Mesh with userData: { faces, faceNumbers, numberToFace }\r\n */\r\nexport function buildDieMesh(sides, color, d6Style) {\r\n const geo = createGeometry(sides);\r\n const mat = new THREE.MeshPhongMaterial({\r\n color,\r\n shininess: 80,\r\n specular: 0x333333,\r\n flatShading: sides !== 6,\r\n });\r\n const mesh = new THREE.Mesh(geo, mat);\r\n\r\n // Edge wireframe\r\n const eg = new THREE.EdgesGeometry(geo);\r\n const em = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.2 });\r\n mesh.add(new THREE.LineSegments(eg, em));\r\n\r\n // Face labels (only for standard platonic solid dice)\r\n if (FACE_LABELED.has(sides)) {\r\n const nf = geomFaceCount(sides);\r\n const faces = computeFaces(geo, nf);\r\n mesh.userData.faces = faces;\r\n const ls = LABEL_SIZE[sides] || 0.3;\r\n const useDots = sides === 6 && d6Style === 'dots';\r\n\r\n const faceNumbers = computeFaceNumbers(faces, sides);\r\n mesh.userData.faceNumbers = faceNumbers;\r\n const numberToFace = {};\r\n faceNumbers.forEach((num, idx) => { numberToFace[num] = idx; });\r\n mesh.userData.numberToFace = numberToFace;\r\n\r\n for (let i = 0; i < sides; i++) {\r\n const face = faces[i];\r\n if (!face) continue;\r\n const num = faceNumbers[i];\r\n const tex = useDots ? getDotTexture(num) : getNumTexture(num);\r\n const pg = new THREE.PlaneGeometry(ls, ls);\r\n const pm = new THREE.MeshBasicMaterial({\r\n map: tex,\r\n transparent: true,\r\n depthWrite: false,\r\n side: THREE.DoubleSide,\r\n });\r\n const label = new THREE.Mesh(pg, pm);\r\n label.position.copy(face.centroid).addScaledVector(face.normal, 0.02);\r\n const sq = faceSettleQuat(face, sides);\r\n label.quaternion.copy(sq.conjugate());\r\n mesh.add(label);\r\n }\r\n }\r\n\r\n return mesh;\r\n}\r\n\r\n/**\r\n * Quaternion that rotates a face normal to point at camera (+Z), text upright.\r\n * Looks up the face index from mesh.userData.numberToFace.\r\n */\r\nexport function settleQuat(mesh, result, sides) {\r\n const faces = mesh.userData.faces;\r\n const ntf = mesh.userData.numberToFace;\r\n if (!faces || !ntf) return null;\r\n const fi = ntf[result];\r\n if (fi == null || fi < 0 || fi >= faces.length) return null;\r\n return faceSettleQuat(faces[fi], sides);\r\n}\r\n","import { useRef, useEffect, useState } from 'react';\r\nimport * as THREE from 'three';\r\nimport {\r\n FACE_LABELED,\r\n SETTLE_SECS,\r\n parseColor,\r\n buildDieMesh,\r\n settleQuat,\r\n} from './diceEngine.js';\r\n\r\n/**\r\n * Dice3D -- A React component that renders 3D dice using Three.js.\r\n *\r\n * Props:\r\n * sides (number) - Number of sides (4, 6, 8, 10, 12, 20, or any)\r\n * color (number | string) - Hex color: 0x3b82f6, '#3b82f6', or Tailwind class\r\n * results (number[]) - Array of roll results, one per die\r\n * isRolling (boolean) - Whether dice are currently rolling\r\n * animationMode ('full' | 'quick' | 'none') - Animation style\r\n * rollTrigger (number) - Increment to trigger a new roll render\r\n * d6Style ('numbers' | 'dots') - D6 label style\r\n * height (number) - Container height in px (auto-computed if omitted)\r\n * className (string) - Optional CSS class for the container\r\n * style (object) - Optional inline styles merged onto container\r\n * emptyText (string) - Text shown when no results (default: 'Press Roll to see 3D dice')\r\n */\r\nconst Dice3D = ({\r\n sides,\r\n color = 0x3b82f6,\r\n results = [],\r\n isRolling = false,\r\n animationMode = 'full',\r\n rollTrigger = 0,\r\n d6Style = 'numbers',\r\n height,\r\n className,\r\n style,\r\n emptyText = 'Press Roll to see 3D dice',\r\n}) => {\r\n const mountRef = useRef(null);\r\n const S = useRef({\r\n scene: null, camera: null, renderer: null,\r\n meshes: [], gridPos: [], animId: null,\r\n phase: 'idle', settleStart: 0, settleData: [],\r\n frustumHalf: 2.5,\r\n });\r\n const rollingRef = useRef(isRolling);\r\n const modeRef = useRef(animationMode);\r\n const numberedRef = useRef(FACE_LABELED.has(sides));\r\n rollingRef.current = isRolling;\r\n modeRef.current = animationMode;\r\n numberedRef.current = FACE_LABELED.has(sides);\r\n\r\n const [overlayPos, setOverlayPos] = useState([]);\r\n const hexColor = parseColor(color);\r\n const isNumbered = FACE_LABELED.has(sides);\r\n\r\n // --- Effect 1: Init scene + persistent animation loop ---\r\n useEffect(() => {\r\n const el = mountRef.current;\r\n if (!el) return;\r\n const s = S.current;\r\n\r\n const scene = new THREE.Scene();\r\n const w0 = el.clientWidth;\r\n const h0 = Math.max(el.clientHeight, 1);\r\n const aspect = w0 / h0;\r\n const fh = 2.5;\r\n const camera = new THREE.OrthographicCamera(\r\n -fh * aspect, fh * aspect, fh, -fh, 0.1, 100\r\n );\r\n camera.position.set(0, 0, 10);\r\n const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });\r\n renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\r\n renderer.setSize(el.clientWidth, el.clientHeight);\r\n renderer.setClearColor(0x000000, 0);\r\n el.appendChild(renderer.domElement);\r\n\r\n scene.add(new THREE.AmbientLight(0xffffff, 0.5));\r\n const kl = new THREE.DirectionalLight(0xffffff, 1.0);\r\n kl.position.set(5, 8, 5);\r\n scene.add(kl);\r\n const fl = new THREE.DirectionalLight(0xffffff, 0.3);\r\n fl.position.set(-3, -2, 4);\r\n scene.add(fl);\r\n\r\n s.scene = scene;\r\n s.camera = camera;\r\n s.renderer = renderer;\r\n\r\n function updateOverlay() {\r\n if (s.gridPos.length === 0) return;\r\n camera.updateProjectionMatrix();\r\n setOverlayPos(s.gridPos.map(gp => {\r\n const v = new THREE.Vector3(gp.x, gp.y, 0).project(camera);\r\n return {\r\n left: ((v.x * 0.5 + 0.5) * 100).toFixed(2) + '%',\r\n top: ((-v.y * 0.5 + 0.5) * 100).toFixed(2) + '%',\r\n };\r\n }));\r\n }\r\n s.updateOverlay = updateOverlay;\r\n\r\n const ro = new ResizeObserver(() => {\r\n const w = el.clientWidth;\r\n const h = el.clientHeight;\r\n if (w === 0 || h === 0) return;\r\n const a = w / h;\r\n const fh2 = s.frustumHalf;\r\n camera.left = -fh2 * a;\r\n camera.right = fh2 * a;\r\n camera.top = fh2;\r\n camera.bottom = -fh2;\r\n camera.updateProjectionMatrix();\r\n renderer.setSize(w, h);\r\n updateOverlay();\r\n });\r\n ro.observe(el);\r\n\r\n const loop = (time) => {\r\n s.animId = requestAnimationFrame(loop);\r\n const t = time / 1000;\r\n const meshes = s.meshes;\r\n const mode = modeRef.current;\r\n const numbered = numberedRef.current;\r\n\r\n if (s.phase === 'spinning' && mode !== 'none') {\r\n const spd = mode === 'full' ? 10 : 15;\r\n meshes.forEach((m, i) => {\r\n m.rotation.x += (spd + i * 2) * 0.016;\r\n m.rotation.y += (spd * 0.8 + i) * 0.016;\r\n m.rotation.z += spd * 0.3 * 0.016;\r\n });\r\n } else if (s.phase === 'settling') {\r\n const elapsed = (time - s.settleStart) / 1000;\r\n const p = Math.min(elapsed / SETTLE_SECS, 1);\r\n const e = 1 - Math.pow(1 - p, 3);\r\n meshes.forEach((m, i) => {\r\n const d = s.settleData[i];\r\n if (d) m.quaternion.slerpQuaternions(d.from, d.to, e);\r\n });\r\n if (p >= 1) s.phase = 'idle';\r\n } else if (s.phase === 'idle' && meshes.length > 0) {\r\n meshes.forEach((m, i) => {\r\n if (numbered && s.settleData[i]) {\r\n const w = new THREE.Quaternion().setFromEuler(new THREE.Euler(\r\n Math.sin(t * 0.8 + i) * 0.03,\r\n Math.sin(t * 0.6 + i * 0.5) * 0.05,\r\n 0\r\n ));\r\n m.quaternion.copy(s.settleData[i].to).multiply(w);\r\n } else {\r\n m.rotation.x += 0.004;\r\n m.rotation.y += 0.006;\r\n }\r\n if (s.gridPos[i]) {\r\n m.position.y = s.gridPos[i].y + Math.sin(t * 1.2 + i * 0.7) * 0.06;\r\n }\r\n });\r\n }\r\n\r\n renderer.render(scene, camera);\r\n };\r\n s.animId = requestAnimationFrame(loop);\r\n\r\n return () => {\r\n ro.disconnect();\r\n if (s.animId) cancelAnimationFrame(s.animId);\r\n s.meshes.forEach(m => {\r\n scene.remove(m);\r\n m.traverse(ch => {\r\n if (ch.geometry) ch.geometry.dispose();\r\n if (ch.material) ch.material.dispose();\r\n });\r\n });\r\n renderer.dispose();\r\n if (renderer.domElement.parentNode === el) el.removeChild(renderer.domElement);\r\n s.scene = null; s.camera = null; s.renderer = null;\r\n s.meshes = []; s.gridPos = []; s.animId = null;\r\n };\r\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\r\n\r\n // --- Effect 2: Build meshes when dice config changes ---\r\n useEffect(() => {\r\n const s = S.current;\r\n if (!s.scene) return;\r\n\r\n s.meshes.forEach(m => {\r\n s.scene.remove(m);\r\n m.traverse(ch => {\r\n if (ch.geometry) ch.geometry.dispose();\r\n if (ch.material) ch.material.dispose();\r\n });\r\n });\r\n s.meshes = [];\r\n s.gridPos = [];\r\n s.settleData = [];\r\n s.phase = 'idle';\r\n\r\n const count = results.length;\r\n if (count === 0) { setOverlayPos([]); return; }\r\n\r\n const cols = Math.min(count, 5);\r\n const rows = Math.ceil(count / cols);\r\n const spacing = 2.8;\r\n\r\n for (let i = 0; i < count; i++) {\r\n const mesh = buildDieMesh(sides, hexColor, d6Style);\r\n const row = Math.floor(i / cols);\r\n const inRow = i - row * cols;\r\n const rowItems = row === rows - 1 ? count - row * cols : cols;\r\n const x = (-(rowItems - 1) / 2 + inRow) * spacing;\r\n const y = ((rows - 1) / 2 - row) * spacing;\r\n\r\n mesh.position.set(x, y, 0);\r\n mesh.rotation.set(\r\n Math.random() * Math.PI * 2,\r\n Math.random() * Math.PI * 2,\r\n Math.random() * Math.PI * 2\r\n );\r\n s.scene.add(mesh);\r\n s.meshes.push(mesh);\r\n s.gridPos.push({ x, y });\r\n }\r\n\r\n const el = mountRef.current;\r\n const spanX = Math.min(count, 5) * spacing;\r\n const spanY = rows * spacing;\r\n const asp = el ? el.clientWidth / Math.max(el.clientHeight, 1) : 1;\r\n const margin = 1.5;\r\n const needY = spanY / 2 + margin;\r\n const needX = (spanX / 2 + margin) / asp;\r\n const fh = Math.max(needY, needX, 2.5);\r\n s.frustumHalf = fh;\r\n s.camera.left = -fh * asp;\r\n s.camera.right = fh * asp;\r\n s.camera.top = fh;\r\n s.camera.bottom = -fh;\r\n s.camera.updateProjectionMatrix();\r\n\r\n if (s.updateOverlay) s.updateOverlay();\r\n\r\n if (!rollingRef.current && isNumbered) {\r\n s.phase = 'settling';\r\n s.settleStart = performance.now();\r\n s.settleData = s.meshes.map((m, i) => ({\r\n from: m.quaternion.clone(),\r\n to: settleQuat(m, results[i], sides) || new THREE.Quaternion(),\r\n }));\r\n }\r\n }, [results.length, sides, hexColor, rollTrigger, isNumbered, d6Style]); // eslint-disable-line react-hooks/exhaustive-deps\r\n\r\n // --- Effect 3: Handle rolling state transitions ---\r\n useEffect(() => {\r\n const s = S.current;\r\n if (isRolling) {\r\n s.phase = 'spinning';\r\n s.settleData = [];\r\n } else if (s.phase === 'spinning' && results.length > 0) {\r\n s.phase = 'settling';\r\n s.settleStart = performance.now();\r\n s.settleData = s.meshes.map((m, i) => {\r\n const from = m.quaternion.clone();\r\n let to = null;\r\n if (isNumbered && m.userData.faces) {\r\n to = settleQuat(m, results[i], sides);\r\n }\r\n if (!to) {\r\n to = new THREE.Quaternion().setFromEuler(\r\n new THREE.Euler(Math.random() * 0.3, Math.random() * 0.3, 0)\r\n );\r\n }\r\n return { from, to };\r\n });\r\n }\r\n }, [isRolling, results, isNumbered, sides]);\r\n\r\n // --- Render ---\r\n const rowCount = Math.ceil(results.length / 5) || 1;\r\n const computedH = Math.max(200, rowCount * 130 + 50);\r\n const canvasH = height != null ? height : computedH;\r\n const showOverlay = !isNumbered && !isRolling && results.length > 0;\r\n const fs = results.length > 10 ? '0.875rem' : results.length > 5 ? '1.1rem' : '1.5rem';\r\n\r\n const containerStyle = {\r\n position: 'relative',\r\n overflow: 'hidden',\r\n borderRadius: '0.75rem',\r\n height: typeof canvasH === 'number' ? canvasH + 'px' : canvasH,\r\n ...style,\r\n };\r\n\r\n return (\r\n <div className={className} style={containerStyle}>\r\n <div ref={mountRef} style={{ width: '100%', height: '100%' }} />\r\n\r\n {/* Overlay numbers for non-face-labeled dice */}\r\n {showOverlay && overlayPos.length === results.length &&\r\n results.map((val, i) => (\r\n <div\r\n key={`${rollTrigger}-${i}`}\r\n style={{\r\n position: 'absolute',\r\n pointerEvents: 'none',\r\n left: overlayPos[i].left,\r\n top: overlayPos[i].top,\r\n transform: 'translate(-50%, -50%)',\r\n zIndex: 10,\r\n animation: 'rdice3d-numIn 0.3s ease-out',\r\n }}\r\n >\r\n <span\r\n style={{\r\n fontWeight: 'bold',\r\n color: 'white',\r\n fontVariantNumeric: 'tabular-nums',\r\n fontSize: fs,\r\n textShadow: '0 2px 8px rgba(0,0,0,0.9), 0 0 4px rgba(0,0,0,0.6)',\r\n }}\r\n >\r\n {val}\r\n </span>\r\n </div>\r\n ))}\r\n\r\n {/* Empty state */}\r\n {results.length === 0 && !isRolling && emptyText && (\r\n <div style={{\r\n position: 'absolute',\r\n inset: 0,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n pointerEvents: 'none',\r\n }}>\r\n <p style={{\r\n fontSize: '1.125rem',\r\n fontWeight: 600,\r\n color: '#9ca3af',\r\n margin: 0,\r\n }}>\r\n {emptyText}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <style>{`\r\n @keyframes rdice3d-numIn {\r\n from { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }\r\n to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\r\n }\r\n `}</style>\r\n </div>\r\n );\r\n};\r\n\r\nexport default Dice3D;\r\n"],"names":["FACE_LABELED","LABEL_SIZE","SETTLE_SECS","D6_NUMBERS","TAILWIND_MAP","parseColor","color","texCache","clearTextureCache","tex","getNumTexture","num","sz","c","ctx","text","fs","THREE","DOT_POSITIONS","getDotTexture","key","dots","r","px","py","createD10Geometry","radius","H","h","top","bot","upper","lower","k","aU","aL","verts","push3","v","nk","geo","createGeometry","sides","geomFaceCount","computeFaces","geometry","numFaces","pos","idx","totalTris","tpf","faces","f","centroid","normal","faceVerts","seenIdx","vertCount","t","base","ia","ib","ic","a","b","clusters","vi","n","mid","found","cl","an","bn","unique","isDup","u","computeFaceNumbers","numbers","target","pairs","used","i","j","p","aA","bA","faceSettleQuat","face","ref","refUp","faceUp","pole","toPole","bestDot","dir","xAxis","m","buildDieMesh","d6Style","mat","mesh","eg","em","nf","ls","useDots","faceNumbers","numberToFace","pg","pm","label","sq","settleQuat","result","ntf","fi","Dice3D","results","isRolling","animationMode","rollTrigger","height","className","style","emptyText","mountRef","useRef","S","rollingRef","modeRef","numberedRef","overlayPos","setOverlayPos","useState","hexColor","isNumbered","useEffect","el","s","scene","w0","h0","aspect","fh","camera","renderer","kl","fl","updateOverlay","gp","ro","w","fh2","loop","time","meshes","mode","numbered","spd","elapsed","e","d","ch","count","cols","rows","spacing","row","inRow","x","y","spanX","spanY","asp","margin","needY","needX","from","to","rowCount","computedH","canvasH","showOverlay","containerStyle","jsxs","jsx","val"],"mappings":"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"}
@@ -0,0 +1,448 @@
1
+ import { jsxs as ce, jsx as W } from "react/jsx-runtime";
2
+ import { useRef as Q, useState as ie, useEffect as Z } from "react";
3
+ import * as o from "three";
4
+ const $ = /* @__PURE__ */ new Set([4, 6, 8, 10, 12, 20]), le = { 4: 0.95, 6: 1.45, 8: 0.82, 10: 0.68, 12: 0.62, 20: 0.56 }, fe = 0.6, ue = [1, 6, 2, 5, 3, 4], J = {
5
+ "bg-purple-500": 11032055,
6
+ "bg-blue-500": 3900150,
7
+ "bg-green-500": 2278750,
8
+ "bg-yellow-500": 15381256,
9
+ "bg-orange-500": 16347926,
10
+ "bg-red-500": 15680580,
11
+ "bg-pink-500": 15485081,
12
+ "bg-gray-500": 7041664
13
+ };
14
+ function de(e) {
15
+ if (typeof e == "number") return e;
16
+ if (typeof e == "string") {
17
+ if (e.startsWith("#")) return parseInt(e.slice(1), 16);
18
+ if (J[e]) return J[e];
19
+ }
20
+ return 7041664;
21
+ }
22
+ const k = /* @__PURE__ */ new Map();
23
+ function Ve() {
24
+ k.forEach((e) => e.dispose()), k.clear();
25
+ }
26
+ function he(e) {
27
+ if (k.has(e)) return k.get(e);
28
+ const d = 256, n = document.createElement("canvas");
29
+ n.width = d, n.height = d;
30
+ const r = n.getContext("2d");
31
+ r.clearRect(0, 0, d, d);
32
+ const h = String(e), s = h.length > 1 ? d * 0.62 : d * 0.82;
33
+ r.font = `bold ${s}px Arial, sans-serif`, r.textAlign = "center", r.textBaseline = "middle", r.fillStyle = "rgba(0,0,0,0.35)", r.fillText(h, d / 2 + 2, d / 2 + 2), r.fillStyle = "#ffffff", r.fillText(h, d / 2, d / 2);
34
+ const f = new o.CanvasTexture(n);
35
+ return f.needsUpdate = !0, k.set(e, f), f;
36
+ }
37
+ const K = {
38
+ 1: [[0.5, 0.5]],
39
+ 2: [[0.22, 0.78], [0.78, 0.22]],
40
+ 3: [[0.22, 0.78], [0.5, 0.5], [0.78, 0.22]],
41
+ 4: [[0.22, 0.22], [0.78, 0.22], [0.22, 0.78], [0.78, 0.78]],
42
+ 5: [[0.22, 0.22], [0.78, 0.22], [0.5, 0.5], [0.22, 0.78], [0.78, 0.78]],
43
+ 6: [[0.22, 0.18], [0.22, 0.5], [0.22, 0.82], [0.78, 0.18], [0.78, 0.5], [0.78, 0.82]]
44
+ };
45
+ function me(e) {
46
+ const d = `dot-${e}`;
47
+ if (k.has(d)) return k.get(d);
48
+ const n = 256, r = document.createElement("canvas");
49
+ r.width = n, r.height = n;
50
+ const h = r.getContext("2d");
51
+ h.clearRect(0, 0, n, n);
52
+ const s = K[e] || K[1], f = e <= 2 ? n * 0.13 : n * 0.105;
53
+ s.forEach(([l, a]) => {
54
+ h.beginPath(), h.arc(l * n + 1, a * n + 1, f, 0, Math.PI * 2), h.fillStyle = "rgba(0,0,0,0.3)", h.fill(), h.beginPath(), h.arc(l * n, a * n, f, 0, Math.PI * 2), h.fillStyle = "#ffffff", h.fill();
55
+ });
56
+ const t = new o.CanvasTexture(r);
57
+ return t.needsUpdate = !0, k.set(d, t), t;
58
+ }
59
+ function pe(e) {
60
+ const d = e, n = d * (1 - Math.cos(Math.PI / 5)) / (1 + Math.cos(Math.PI / 5)), r = e * 0.85, h = [0, d, 0], s = [0, -d, 0], f = [], t = [];
61
+ for (let u = 0; u < 5; u++) {
62
+ const g = u * 2 * Math.PI / 5, b = g + Math.PI / 5;
63
+ f.push([r * Math.cos(g), n, r * Math.sin(g)]), t.push([r * Math.cos(b), -n, r * Math.sin(b)]);
64
+ }
65
+ const l = [];
66
+ function a(u) {
67
+ l.push(u[0], u[1], u[2]);
68
+ }
69
+ for (let u = 0; u < 5; u++) {
70
+ const g = (u + 1) % 5;
71
+ a(h), a(t[u]), a(f[u]), a(h), a(f[g]), a(t[u]), a(s), a(t[u]), a(f[g]), a(s), a(f[g]), a(t[g]);
72
+ }
73
+ const m = new o.BufferGeometry();
74
+ return m.setAttribute("position", new o.Float32BufferAttribute(l, 3)), m.computeVertexNormals(), m;
75
+ }
76
+ function ge(e) {
77
+ switch (e) {
78
+ case 4:
79
+ return new o.TetrahedronGeometry(1.2, 0);
80
+ case 6:
81
+ return new o.BoxGeometry(1.7, 1.7, 1.7);
82
+ case 8:
83
+ return new o.OctahedronGeometry(1.2, 0);
84
+ case 10:
85
+ return pe(1.2);
86
+ case 12:
87
+ return new o.DodecahedronGeometry(1.2, 0);
88
+ case 20:
89
+ return new o.IcosahedronGeometry(1.2, 0);
90
+ default:
91
+ return new o.IcosahedronGeometry(1.14, 0);
92
+ }
93
+ }
94
+ function we(e) {
95
+ return e === 4 ? 4 : e === 6 ? 6 : e === 8 ? 8 : e === 10 ? 10 : e === 12 ? 12 : 20;
96
+ }
97
+ function be(e, d) {
98
+ const n = e.getAttribute("position"), r = e.index;
99
+ if (r) {
100
+ const f = r.count / 3, t = Math.round(f / d), l = [];
101
+ for (let a = 0; a < d; a++) {
102
+ const m = new o.Vector3(), u = new o.Vector3(), g = [], b = /* @__PURE__ */ new Set();
103
+ let x = 0;
104
+ for (let y = a * t; y < (a + 1) * t; y++) {
105
+ const A = y * 3, T = r.getX(A), B = r.getX(A + 1), D = r.getX(A + 2), z = new o.Vector3().fromBufferAttribute(n, T), L = new o.Vector3().fromBufferAttribute(n, B), G = new o.Vector3().fromBufferAttribute(n, D);
106
+ m.add(z).add(L).add(G), x += 3, y === a * t && u.crossVectors(
107
+ new o.Vector3().subVectors(L, z),
108
+ new o.Vector3().subVectors(G, z)
109
+ ).normalize(), b.has(T) || (b.add(T), g.push(z)), b.has(B) || (b.add(B), g.push(L)), b.has(D) || (b.add(D), g.push(G));
110
+ }
111
+ m.divideScalar(x), u.dot(m) < 0 && u.negate(), l.push({ centroid: m.clone(), normal: u.clone(), verts: g });
112
+ }
113
+ return l;
114
+ }
115
+ const h = n.count / 3, s = [];
116
+ for (let f = 0; f < h; f++) {
117
+ const t = f * 3, l = new o.Vector3().fromBufferAttribute(n, t), a = new o.Vector3().fromBufferAttribute(n, t + 1), m = new o.Vector3().fromBufferAttribute(n, t + 2), u = new o.Vector3().crossVectors(
118
+ new o.Vector3().subVectors(a, l),
119
+ new o.Vector3().subVectors(m, l)
120
+ ).normalize(), g = new o.Vector3().add(l).add(a).add(m).divideScalar(3);
121
+ u.dot(g) < 0 && u.negate();
122
+ let b = !1;
123
+ for (const x of s)
124
+ if (x.normal.dot(u) > 0.999) {
125
+ x.verts.push(l, a, m), b = !0;
126
+ break;
127
+ }
128
+ b || s.push({ normal: u.clone(), verts: [l, a, m] });
129
+ }
130
+ return s.sort((f, t) => {
131
+ const l = f.normal, a = t.normal;
132
+ return Math.abs(l.x - a.x) > 1e-3 ? l.x - a.x : Math.abs(l.y - a.y) > 1e-3 ? l.y - a.y : l.z - a.z;
133
+ }), s.map((f) => {
134
+ const t = new o.Vector3();
135
+ f.verts.forEach((a) => t.add(a)), t.divideScalar(f.verts.length);
136
+ const l = [];
137
+ for (const a of f.verts) {
138
+ let m = !1;
139
+ for (const u of l)
140
+ if (a.distanceTo(u) < 1e-3) {
141
+ m = !0;
142
+ break;
143
+ }
144
+ m || l.push(a.clone());
145
+ }
146
+ return { centroid: t.clone(), normal: f.normal.clone(), verts: l };
147
+ });
148
+ }
149
+ function xe(e, d) {
150
+ if (d === 6) return ue;
151
+ if (d === 4) return [1, 2, 3, 4];
152
+ const n = e.length, r = new Array(n).fill(0), h = d + 1, s = [], f = /* @__PURE__ */ new Set();
153
+ for (let t = 0; t < n; t++)
154
+ if (!f.has(t)) {
155
+ for (let l = t + 1; l < n; l++)
156
+ if (!f.has(l) && e[t].normal.dot(e[l].normal) < -0.99) {
157
+ s.push([t, l]), f.add(t), f.add(l);
158
+ break;
159
+ }
160
+ f.has(t) || (s.push([t, -1]), f.add(t));
161
+ }
162
+ if (d === 10) {
163
+ s.forEach((t) => {
164
+ t[1] >= 0 && e[t[0]].centroid.y < e[t[1]].centroid.y && ([t[0], t[1]] = [t[1], t[0]]);
165
+ }), s.sort((t, l) => {
166
+ const a = Math.atan2(e[t[0]].centroid.z, e[t[0]].centroid.x), m = Math.atan2(e[l[0]].centroid.z, e[l[0]].centroid.x);
167
+ return a - m;
168
+ });
169
+ for (let t = 0; t < s.length; t++)
170
+ r[s[t][0]] = t * 2 + 1, s[t][1] >= 0 && (r[s[t][1]] = h - (t * 2 + 1));
171
+ return r;
172
+ }
173
+ for (let t = 0; t < s.length; t++)
174
+ r[s[t][0]] = t + 1, s[t][1] >= 0 && (r[s[t][1]] = h - (t + 1));
175
+ return r;
176
+ }
177
+ function te(e, d) {
178
+ const n = e.normal.clone().normalize();
179
+ let r = new o.Vector3(0, 1, 0);
180
+ Math.abs(n.dot(r)) > 0.99 && (r = new o.Vector3(0, 0, 1));
181
+ const h = r.clone().addScaledVector(n, -r.dot(n)).normalize();
182
+ let s;
183
+ if (d === 10) {
184
+ const a = e.centroid.y > 0 ? new o.Vector3(0, 1.2, 0) : new o.Vector3(0, -1.2, 0), m = new o.Vector3().subVectors(a, e.centroid);
185
+ m.addScaledVector(n, -m.dot(n)).normalize(), s = m.negate();
186
+ } else if (d !== 6 && e.verts && e.verts.length >= 3) {
187
+ let l = -1 / 0;
188
+ s = h;
189
+ for (const a of e.verts) {
190
+ const m = new o.Vector3().subVectors(a, e.centroid).normalize(), u = m.dot(h);
191
+ u > l && (l = u, s = m);
192
+ }
193
+ } else
194
+ s = h;
195
+ const f = new o.Vector3().crossVectors(s, n).normalize(), t = new o.Matrix4().makeBasis(f, s, n);
196
+ return new o.Quaternion().setFromRotationMatrix(t).conjugate();
197
+ }
198
+ function ye(e, d, n) {
199
+ const r = ge(e), h = new o.MeshPhongMaterial({
200
+ color: d,
201
+ shininess: 80,
202
+ specular: 3355443,
203
+ flatShading: e !== 6
204
+ }), s = new o.Mesh(r, h), f = new o.EdgesGeometry(r), t = new o.LineBasicMaterial({ color: 16777215, transparent: !0, opacity: 0.2 });
205
+ if (s.add(new o.LineSegments(f, t)), $.has(e)) {
206
+ const l = we(e), a = be(r, l);
207
+ s.userData.faces = a;
208
+ const m = le[e] || 0.3, u = e === 6 && n === "dots", g = xe(a, e);
209
+ s.userData.faceNumbers = g;
210
+ const b = {};
211
+ g.forEach((x, y) => {
212
+ b[x] = y;
213
+ }), s.userData.numberToFace = b;
214
+ for (let x = 0; x < e; x++) {
215
+ const y = a[x];
216
+ if (!y) continue;
217
+ const A = g[x], T = u ? me(A) : he(A), B = new o.PlaneGeometry(m, m), D = new o.MeshBasicMaterial({
218
+ map: T,
219
+ transparent: !0,
220
+ depthWrite: !1,
221
+ side: o.DoubleSide
222
+ }), z = new o.Mesh(B, D);
223
+ z.position.copy(y.centroid).addScaledVector(y.normal, 0.02);
224
+ const L = te(y, e);
225
+ z.quaternion.copy(L.conjugate()), s.add(z);
226
+ }
227
+ }
228
+ return s;
229
+ }
230
+ function ee(e, d, n) {
231
+ const r = e.userData.faces, h = e.userData.numberToFace;
232
+ if (!r || !h) return null;
233
+ const s = h[d];
234
+ return s == null || s < 0 || s >= r.length ? null : te(r[s], n);
235
+ }
236
+ const Se = ({
237
+ sides: e,
238
+ color: d = 3900150,
239
+ results: n = [],
240
+ isRolling: r = !1,
241
+ animationMode: h = "full",
242
+ rollTrigger: s = 0,
243
+ d6Style: f = "numbers",
244
+ height: t,
245
+ className: l,
246
+ style: a,
247
+ emptyText: m = "Press Roll to see 3D dice"
248
+ }) => {
249
+ const u = Q(null), g = Q({
250
+ scene: null,
251
+ camera: null,
252
+ renderer: null,
253
+ meshes: [],
254
+ gridPos: [],
255
+ animId: null,
256
+ phase: "idle",
257
+ settleStart: 0,
258
+ settleData: [],
259
+ frustumHalf: 2.5
260
+ }), b = Q(r), x = Q(h), y = Q($.has(e));
261
+ b.current = r, x.current = h, y.current = $.has(e);
262
+ const [A, T] = ie([]), B = de(d), D = $.has(e);
263
+ Z(() => {
264
+ const c = u.current;
265
+ if (!c) return;
266
+ const i = g.current, M = new o.Scene(), O = c.clientWidth, I = Math.max(c.clientHeight, 1), R = O / I, N = 2.5, P = new o.OrthographicCamera(
267
+ -N * R,
268
+ N * R,
269
+ N,
270
+ -N,
271
+ 0.1,
272
+ 100
273
+ );
274
+ P.position.set(0, 0, 10);
275
+ const v = new o.WebGLRenderer({ antialias: !0, alpha: !0 });
276
+ v.setPixelRatio(Math.min(window.devicePixelRatio, 2)), v.setSize(c.clientWidth, c.clientHeight), v.setClearColor(0, 0), c.appendChild(v.domElement), M.add(new o.AmbientLight(16777215, 0.5));
277
+ const q = new o.DirectionalLight(16777215, 1);
278
+ q.position.set(5, 8, 5), M.add(q);
279
+ const U = new o.DirectionalLight(16777215, 0.3);
280
+ U.position.set(-3, -2, 4), M.add(U), i.scene = M, i.camera = P, i.renderer = v;
281
+ function _() {
282
+ i.gridPos.length !== 0 && (P.updateProjectionMatrix(), T(i.gridPos.map((p) => {
283
+ const w = new o.Vector3(p.x, p.y, 0).project(P);
284
+ return {
285
+ left: ((w.x * 0.5 + 0.5) * 100).toFixed(2) + "%",
286
+ top: ((-w.y * 0.5 + 0.5) * 100).toFixed(2) + "%"
287
+ };
288
+ })));
289
+ }
290
+ i.updateOverlay = _;
291
+ const F = new ResizeObserver(() => {
292
+ const p = c.clientWidth, w = c.clientHeight;
293
+ if (p === 0 || w === 0) return;
294
+ const C = p / w, H = i.frustumHalf;
295
+ P.left = -H * C, P.right = H * C, P.top = H, P.bottom = -H, P.updateProjectionMatrix(), v.setSize(p, w), _();
296
+ });
297
+ F.observe(c);
298
+ const S = (p) => {
299
+ i.animId = requestAnimationFrame(S);
300
+ const w = p / 1e3, C = i.meshes, H = x.current, X = y.current;
301
+ if (i.phase === "spinning" && H !== "none") {
302
+ const E = H === "full" ? 10 : 15;
303
+ C.forEach((V, j) => {
304
+ V.rotation.x += (E + j * 2) * 0.016, V.rotation.y += (E * 0.8 + j) * 0.016, V.rotation.z += E * 0.3 * 0.016;
305
+ });
306
+ } else if (i.phase === "settling") {
307
+ const E = (p - i.settleStart) / 1e3, V = Math.min(E / fe, 1), j = 1 - Math.pow(1 - V, 3);
308
+ C.forEach((ae, se) => {
309
+ const Y = i.settleData[se];
310
+ Y && ae.quaternion.slerpQuaternions(Y.from, Y.to, j);
311
+ }), V >= 1 && (i.phase = "idle");
312
+ } else i.phase === "idle" && C.length > 0 && C.forEach((E, V) => {
313
+ if (X && i.settleData[V]) {
314
+ const j = new o.Quaternion().setFromEuler(new o.Euler(
315
+ Math.sin(w * 0.8 + V) * 0.03,
316
+ Math.sin(w * 0.6 + V * 0.5) * 0.05,
317
+ 0
318
+ ));
319
+ E.quaternion.copy(i.settleData[V].to).multiply(j);
320
+ } else
321
+ E.rotation.x += 4e-3, E.rotation.y += 6e-3;
322
+ i.gridPos[V] && (E.position.y = i.gridPos[V].y + Math.sin(w * 1.2 + V * 0.7) * 0.06);
323
+ });
324
+ v.render(M, P);
325
+ };
326
+ return i.animId = requestAnimationFrame(S), () => {
327
+ F.disconnect(), i.animId && cancelAnimationFrame(i.animId), i.meshes.forEach((p) => {
328
+ M.remove(p), p.traverse((w) => {
329
+ w.geometry && w.geometry.dispose(), w.material && w.material.dispose();
330
+ });
331
+ }), v.dispose(), v.domElement.parentNode === c && c.removeChild(v.domElement), i.scene = null, i.camera = null, i.renderer = null, i.meshes = [], i.gridPos = [], i.animId = null;
332
+ };
333
+ }, []), Z(() => {
334
+ const c = g.current;
335
+ if (!c.scene) return;
336
+ c.meshes.forEach((S) => {
337
+ c.scene.remove(S), S.traverse((p) => {
338
+ p.geometry && p.geometry.dispose(), p.material && p.material.dispose();
339
+ });
340
+ }), c.meshes = [], c.gridPos = [], c.settleData = [], c.phase = "idle";
341
+ const i = n.length;
342
+ if (i === 0) {
343
+ T([]);
344
+ return;
345
+ }
346
+ const M = Math.min(i, 5), O = Math.ceil(i / M), I = 2.8;
347
+ for (let S = 0; S < i; S++) {
348
+ const p = ye(e, B, f), w = Math.floor(S / M), C = S - w * M, X = (-((w === O - 1 ? i - w * M : M) - 1) / 2 + C) * I, E = ((O - 1) / 2 - w) * I;
349
+ p.position.set(X, E, 0), p.rotation.set(
350
+ Math.random() * Math.PI * 2,
351
+ Math.random() * Math.PI * 2,
352
+ Math.random() * Math.PI * 2
353
+ ), c.scene.add(p), c.meshes.push(p), c.gridPos.push({ x: X, y: E });
354
+ }
355
+ const R = u.current, N = Math.min(i, 5) * I, P = O * I, v = R ? R.clientWidth / Math.max(R.clientHeight, 1) : 1, q = 1.5, U = P / 2 + q, _ = (N / 2 + q) / v, F = Math.max(U, _, 2.5);
356
+ c.frustumHalf = F, c.camera.left = -F * v, c.camera.right = F * v, c.camera.top = F, c.camera.bottom = -F, c.camera.updateProjectionMatrix(), c.updateOverlay && c.updateOverlay(), !b.current && D && (c.phase = "settling", c.settleStart = performance.now(), c.settleData = c.meshes.map((S, p) => ({
357
+ from: S.quaternion.clone(),
358
+ to: ee(S, n[p], e) || new o.Quaternion()
359
+ })));
360
+ }, [n.length, e, B, s, D, f]), Z(() => {
361
+ const c = g.current;
362
+ r ? (c.phase = "spinning", c.settleData = []) : c.phase === "spinning" && n.length > 0 && (c.phase = "settling", c.settleStart = performance.now(), c.settleData = c.meshes.map((i, M) => {
363
+ const O = i.quaternion.clone();
364
+ let I = null;
365
+ return D && i.userData.faces && (I = ee(i, n[M], e)), I || (I = new o.Quaternion().setFromEuler(
366
+ new o.Euler(Math.random() * 0.3, Math.random() * 0.3, 0)
367
+ )), { from: O, to: I };
368
+ }));
369
+ }, [r, n, D, e]);
370
+ const z = Math.ceil(n.length / 5) || 1, L = Math.max(200, z * 130 + 50), G = t ?? L, ne = !D && !r && n.length > 0, oe = n.length > 10 ? "0.875rem" : n.length > 5 ? "1.1rem" : "1.5rem", re = {
371
+ position: "relative",
372
+ overflow: "hidden",
373
+ borderRadius: "0.75rem",
374
+ height: typeof G == "number" ? G + "px" : G,
375
+ ...a
376
+ };
377
+ return /* @__PURE__ */ ce("div", { className: l, style: re, children: [
378
+ /* @__PURE__ */ W("div", { ref: u, style: { width: "100%", height: "100%" } }),
379
+ ne && A.length === n.length && n.map((c, i) => /* @__PURE__ */ W(
380
+ "div",
381
+ {
382
+ style: {
383
+ position: "absolute",
384
+ pointerEvents: "none",
385
+ left: A[i].left,
386
+ top: A[i].top,
387
+ transform: "translate(-50%, -50%)",
388
+ zIndex: 10,
389
+ animation: "rdice3d-numIn 0.3s ease-out"
390
+ },
391
+ children: /* @__PURE__ */ W(
392
+ "span",
393
+ {
394
+ style: {
395
+ fontWeight: "bold",
396
+ color: "white",
397
+ fontVariantNumeric: "tabular-nums",
398
+ fontSize: oe,
399
+ textShadow: "0 2px 8px rgba(0,0,0,0.9), 0 0 4px rgba(0,0,0,0.6)"
400
+ },
401
+ children: c
402
+ }
403
+ )
404
+ },
405
+ `${s}-${i}`
406
+ )),
407
+ n.length === 0 && !r && m && /* @__PURE__ */ W("div", { style: {
408
+ position: "absolute",
409
+ inset: 0,
410
+ display: "flex",
411
+ alignItems: "center",
412
+ justifyContent: "center",
413
+ pointerEvents: "none"
414
+ }, children: /* @__PURE__ */ W("p", { style: {
415
+ fontSize: "1.125rem",
416
+ fontWeight: 600,
417
+ color: "#9ca3af",
418
+ margin: 0
419
+ }, children: m }) }),
420
+ /* @__PURE__ */ W("style", { children: `
421
+ @keyframes rdice3d-numIn {
422
+ from { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }
423
+ to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
424
+ }
425
+ ` })
426
+ ] });
427
+ };
428
+ export {
429
+ ue as D6_NUMBERS,
430
+ Se as Dice3D,
431
+ $ as FACE_LABELED,
432
+ le as LABEL_SIZE,
433
+ fe as SETTLE_SECS,
434
+ ye as buildDieMesh,
435
+ Ve as clearTextureCache,
436
+ xe as computeFaceNumbers,
437
+ be as computeFaces,
438
+ pe as createD10Geometry,
439
+ ge as createGeometry,
440
+ Se as default,
441
+ te as faceSettleQuat,
442
+ we as geomFaceCount,
443
+ me as getDotTexture,
444
+ he as getNumTexture,
445
+ de as parseColor,
446
+ ee as settleQuat
447
+ };
448
+ //# sourceMappingURL=react-3d-dice.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-3d-dice.es.js","sources":["../src/diceEngine.js","../src/Dice3D.jsx"],"sourcesContent":["import * as THREE from 'three';\r\n\r\n// --- Constants ---\r\n\r\n/** Dice with exact face count match get canvas-textured face labels */\r\nexport const FACE_LABELED = new Set([4, 6, 8, 10, 12, 20]);\r\n\r\n/** Label plane size per die type (world-space units) */\r\nexport const LABEL_SIZE = { 4: 0.95, 6: 1.45, 8: 0.82, 10: 0.68, 12: 0.62, 20: 0.56 };\r\n\r\n/** Settle animation duration in seconds */\r\nexport const SETTLE_SECS = 0.6;\r\n\r\n/** D6 standard: opposite faces sum to 7. BoxGeometry face order: +x,-x,+y,-y,+z,-z */\r\nexport const D6_NUMBERS = [1, 6, 2, 5, 3, 4];\r\n\r\n// --- Color helpers ---\r\n\r\n/**\r\n * Parse a color value into a Three.js-compatible hex integer.\r\n * Accepts: hex number (0x3b82f6), CSS hex string ('#3b82f6'), or Tailwind class (bg-blue-500).\r\n */\r\nconst TAILWIND_MAP = {\r\n 'bg-purple-500': 0xa855f7,\r\n 'bg-blue-500': 0x3b82f6,\r\n 'bg-green-500': 0x22c55e,\r\n 'bg-yellow-500': 0xeab308,\r\n 'bg-orange-500': 0xf97316,\r\n 'bg-red-500': 0xef4444,\r\n 'bg-pink-500': 0xec4899,\r\n 'bg-gray-500': 0x6b7280,\r\n};\r\n\r\nexport function parseColor(color) {\r\n if (typeof color === 'number') return color;\r\n if (typeof color === 'string') {\r\n if (color.startsWith('#')) return parseInt(color.slice(1), 16);\r\n if (TAILWIND_MAP[color]) return TAILWIND_MAP[color];\r\n }\r\n return 0x6b7280;\r\n}\r\n\r\n// --- Texture cache ---\r\n\r\nconst texCache = new Map();\r\n\r\n/** Clear the texture cache (useful for memory cleanup) */\r\nexport function clearTextureCache() {\r\n texCache.forEach(tex => tex.dispose());\r\n texCache.clear();\r\n}\r\n\r\n/** Get or create a canvas texture with a number label */\r\nexport function getNumTexture(num) {\r\n if (texCache.has(num)) return texCache.get(num);\r\n const sz = 256;\r\n const c = document.createElement('canvas');\r\n c.width = sz;\r\n c.height = sz;\r\n const ctx = c.getContext('2d');\r\n ctx.clearRect(0, 0, sz, sz);\r\n const text = String(num);\r\n const fs = text.length > 1 ? sz * 0.62 : sz * 0.82;\r\n ctx.font = `bold ${fs}px Arial, sans-serif`;\r\n ctx.textAlign = 'center';\r\n ctx.textBaseline = 'middle';\r\n // Shadow for depth\r\n ctx.fillStyle = 'rgba(0,0,0,0.35)';\r\n ctx.fillText(text, sz / 2 + 2, sz / 2 + 2);\r\n // White number\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fillText(text, sz / 2, sz / 2);\r\n const tex = new THREE.CanvasTexture(c);\r\n tex.needsUpdate = true;\r\n texCache.set(num, tex);\r\n return tex;\r\n}\r\n\r\n// D6 dot/pip patterns (traditional die face layouts)\r\nconst DOT_POSITIONS = {\r\n 1: [[0.5, 0.5]],\r\n 2: [[0.22, 0.78], [0.78, 0.22]],\r\n 3: [[0.22, 0.78], [0.5, 0.5], [0.78, 0.22]],\r\n 4: [[0.22, 0.22], [0.78, 0.22], [0.22, 0.78], [0.78, 0.78]],\r\n 5: [[0.22, 0.22], [0.78, 0.22], [0.5, 0.5], [0.22, 0.78], [0.78, 0.78]],\r\n 6: [[0.22, 0.18], [0.22, 0.5], [0.22, 0.82], [0.78, 0.18], [0.78, 0.5], [0.78, 0.82]],\r\n};\r\n\r\n/** Get or create a canvas texture with dot pips (D6 traditional style) */\r\nexport function getDotTexture(num) {\r\n const key = `dot-${num}`;\r\n if (texCache.has(key)) return texCache.get(key);\r\n const sz = 256;\r\n const c = document.createElement('canvas');\r\n c.width = sz;\r\n c.height = sz;\r\n const ctx = c.getContext('2d');\r\n ctx.clearRect(0, 0, sz, sz);\r\n const dots = DOT_POSITIONS[num] || DOT_POSITIONS[1];\r\n const r = num <= 2 ? sz * 0.13 : sz * 0.105;\r\n dots.forEach(([px, py]) => {\r\n // Shadow\r\n ctx.beginPath();\r\n ctx.arc(px * sz + 1, py * sz + 1, r, 0, Math.PI * 2);\r\n ctx.fillStyle = 'rgba(0,0,0,0.3)';\r\n ctx.fill();\r\n // White dot\r\n ctx.beginPath();\r\n ctx.arc(px * sz, py * sz, r, 0, Math.PI * 2);\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fill();\r\n });\r\n const tex = new THREE.CanvasTexture(c);\r\n tex.needsUpdate = true;\r\n texCache.set(key, tex);\r\n return tex;\r\n}\r\n\r\n// --- Geometry helpers ---\r\n\r\n/**\r\n * Pentagonal trapezohedron: the standard D10 shape.\r\n * 12 vertices (2 poles + 5 upper ring + 5 lower ring), 10 kite-shaped faces.\r\n * Each kite is split into 2 coplanar triangles for BufferGeometry.\r\n * Planarity condition: h/H = (1 - cos36) / (1 + cos36)\r\n */\r\nexport function createD10Geometry(radius) {\r\n const H = radius;\r\n const h = H * (1 - Math.cos(Math.PI / 5)) / (1 + Math.cos(Math.PI / 5));\r\n const r = radius * 0.85;\r\n\r\n const top = [0, H, 0];\r\n const bot = [0, -H, 0];\r\n const upper = [];\r\n const lower = [];\r\n\r\n for (let k = 0; k < 5; k++) {\r\n const aU = (k * 2 * Math.PI) / 5;\r\n const aL = aU + Math.PI / 5;\r\n upper.push([r * Math.cos(aU), h, r * Math.sin(aU)]);\r\n lower.push([r * Math.cos(aL), -h, r * Math.sin(aL)]);\r\n }\r\n\r\n const verts = [];\r\n function push3(v) { verts.push(v[0], v[1], v[2]); }\r\n\r\n for (let k = 0; k < 5; k++) {\r\n const nk = (k + 1) % 5;\r\n // Top kite: top, upper[k], lower[k], upper[nk]\r\n push3(top); push3(lower[k]); push3(upper[k]);\r\n push3(top); push3(upper[nk]); push3(lower[k]);\r\n // Bottom kite: bot, lower[k], upper[nk], lower[nk]\r\n push3(bot); push3(lower[k]); push3(upper[nk]);\r\n push3(bot); push3(upper[nk]); push3(lower[nk]);\r\n }\r\n\r\n const geo = new THREE.BufferGeometry();\r\n geo.setAttribute('position', new THREE.Float32BufferAttribute(verts, 3));\r\n geo.computeVertexNormals();\r\n return geo;\r\n}\r\n\r\n/** Create geometry for a given number of sides */\r\nexport function createGeometry(sides) {\r\n switch (sides) {\r\n case 4: return new THREE.TetrahedronGeometry(1.2, 0);\r\n case 6: return new THREE.BoxGeometry(1.7, 1.7, 1.7);\r\n case 8: return new THREE.OctahedronGeometry(1.2, 0);\r\n case 10: return createD10Geometry(1.2);\r\n case 12: return new THREE.DodecahedronGeometry(1.2, 0);\r\n case 20: return new THREE.IcosahedronGeometry(1.2, 0);\r\n default: return new THREE.IcosahedronGeometry(1.14, 0);\r\n }\r\n}\r\n\r\n/** How many geometric faces for each geometry type */\r\nexport function geomFaceCount(sides) {\r\n if (sides === 4) return 4;\r\n if (sides === 6) return 6;\r\n if (sides === 8) return 8;\r\n if (sides === 10) return 10;\r\n if (sides === 12) return 12;\r\n if (sides === 20) return 20;\r\n return 20;\r\n}\r\n\r\n/**\r\n * Compute centroid + outward normal + unique vertices per logical face.\r\n * For indexed geometry (D6 BoxGeometry) uses sequential grouping.\r\n * For non-indexed polyhedra, clusters triangles by normal direction since\r\n * Three.js does NOT group triangles sequentially by polygon face.\r\n */\r\nexport function computeFaces(geometry, numFaces) {\r\n const pos = geometry.getAttribute('position');\r\n const idx = geometry.index;\r\n\r\n // --- Indexed geometry (BoxGeometry / D6): sequential grouping ---\r\n if (idx) {\r\n const totalTris = idx.count / 3;\r\n const tpf = Math.round(totalTris / numFaces);\r\n const faces = [];\r\n for (let f = 0; f < numFaces; f++) {\r\n const centroid = new THREE.Vector3();\r\n const normal = new THREE.Vector3();\r\n const faceVerts = [];\r\n const seenIdx = new Set();\r\n let vertCount = 0;\r\n for (let t = f * tpf; t < (f + 1) * tpf; t++) {\r\n const base = t * 3;\r\n const ia = idx.getX(base), ib = idx.getX(base + 1), ic = idx.getX(base + 2);\r\n const a = new THREE.Vector3().fromBufferAttribute(pos, ia);\r\n const b = new THREE.Vector3().fromBufferAttribute(pos, ib);\r\n const c = new THREE.Vector3().fromBufferAttribute(pos, ic);\r\n centroid.add(a).add(b).add(c);\r\n vertCount += 3;\r\n if (t === f * tpf) {\r\n normal.crossVectors(\r\n new THREE.Vector3().subVectors(b, a),\r\n new THREE.Vector3().subVectors(c, a)\r\n ).normalize();\r\n }\r\n if (!seenIdx.has(ia)) { seenIdx.add(ia); faceVerts.push(a); }\r\n if (!seenIdx.has(ib)) { seenIdx.add(ib); faceVerts.push(b); }\r\n if (!seenIdx.has(ic)) { seenIdx.add(ic); faceVerts.push(c); }\r\n }\r\n centroid.divideScalar(vertCount);\r\n if (normal.dot(centroid) < 0) normal.negate();\r\n faces.push({ centroid: centroid.clone(), normal: normal.clone(), verts: faceVerts });\r\n }\r\n return faces;\r\n }\r\n\r\n // --- Non-indexed polyhedra: cluster triangles by normal direction ---\r\n const totalTris = pos.count / 3;\r\n const clusters = [];\r\n\r\n for (let t = 0; t < totalTris; t++) {\r\n const vi = t * 3;\r\n const a = new THREE.Vector3().fromBufferAttribute(pos, vi);\r\n const b = new THREE.Vector3().fromBufferAttribute(pos, vi + 1);\r\n const c = new THREE.Vector3().fromBufferAttribute(pos, vi + 2);\r\n const n = new THREE.Vector3().crossVectors(\r\n new THREE.Vector3().subVectors(b, a),\r\n new THREE.Vector3().subVectors(c, a)\r\n ).normalize();\r\n const mid = new THREE.Vector3().add(a).add(b).add(c).divideScalar(3);\r\n if (n.dot(mid) < 0) n.negate();\r\n\r\n let found = false;\r\n for (const cl of clusters) {\r\n if (cl.normal.dot(n) > 0.999) {\r\n cl.verts.push(a, b, c);\r\n found = true;\r\n break;\r\n }\r\n }\r\n if (!found) {\r\n clusters.push({ normal: n.clone(), verts: [a, b, c] });\r\n }\r\n }\r\n\r\n clusters.sort((a, b) => {\r\n const an = a.normal, bn = b.normal;\r\n if (Math.abs(an.x - bn.x) > 0.001) return an.x - bn.x;\r\n if (Math.abs(an.y - bn.y) > 0.001) return an.y - bn.y;\r\n return an.z - bn.z;\r\n });\r\n\r\n return clusters.map(cl => {\r\n const centroid = new THREE.Vector3();\r\n cl.verts.forEach(v => centroid.add(v));\r\n centroid.divideScalar(cl.verts.length);\r\n const unique = [];\r\n for (const v of cl.verts) {\r\n let isDup = false;\r\n for (const u of unique) {\r\n if (v.distanceTo(u) < 0.001) { isDup = true; break; }\r\n }\r\n if (!isDup) unique.push(v.clone());\r\n }\r\n return { centroid: centroid.clone(), normal: cl.normal.clone(), verts: unique };\r\n });\r\n}\r\n\r\n/**\r\n * Assign display numbers to faces so opposite faces sum to sides+1.\r\n * D6 uses hardcoded D6_NUMBERS. D4 has no opposite faces. D10 puts odds at top pole.\r\n */\r\nexport function computeFaceNumbers(faces, sides) {\r\n if (sides === 6) return D6_NUMBERS;\r\n if (sides === 4) return [1, 2, 3, 4];\r\n\r\n const n = faces.length;\r\n const numbers = new Array(n).fill(0);\r\n const target = sides + 1;\r\n\r\n // Find opposite pairs: normals with dot < -0.99\r\n const pairs = [];\r\n const used = new Set();\r\n for (let i = 0; i < n; i++) {\r\n if (used.has(i)) continue;\r\n for (let j = i + 1; j < n; j++) {\r\n if (used.has(j)) continue;\r\n if (faces[i].normal.dot(faces[j].normal) < -0.99) {\r\n pairs.push([i, j]);\r\n used.add(i);\r\n used.add(j);\r\n break;\r\n }\r\n }\r\n if (!used.has(i)) {\r\n pairs.push([i, -1]);\r\n used.add(i);\r\n }\r\n }\r\n\r\n if (sides === 10) {\r\n pairs.forEach(p => {\r\n if (p[1] >= 0 && faces[p[0]].centroid.y < faces[p[1]].centroid.y) {\r\n [p[0], p[1]] = [p[1], p[0]];\r\n }\r\n });\r\n pairs.sort((a, b) => {\r\n const aA = Math.atan2(faces[a[0]].centroid.z, faces[a[0]].centroid.x);\r\n const bA = Math.atan2(faces[b[0]].centroid.z, faces[b[0]].centroid.x);\r\n return aA - bA;\r\n });\r\n for (let k = 0; k < pairs.length; k++) {\r\n numbers[pairs[k][0]] = k * 2 + 1;\r\n if (pairs[k][1] >= 0) numbers[pairs[k][1]] = target - (k * 2 + 1);\r\n }\r\n return numbers;\r\n }\r\n\r\n for (let k = 0; k < pairs.length; k++) {\r\n numbers[pairs[k][0]] = k + 1;\r\n if (pairs[k][1] >= 0) numbers[pairs[k][1]] = target - (k + 1);\r\n }\r\n return numbers;\r\n}\r\n\r\n/**\r\n * Compute the settle quaternion for a face: rotates face normal to +Z, text upright.\r\n * Builds a full rotation matrix (not shortest-arc) so text aligns with face geometry.\r\n * D10: pole tip at bottom. D6: project +Y (edge-aligned). Others: snap to nearest vertex.\r\n */\r\nexport function faceSettleQuat(face, sides) {\r\n const n = face.normal.clone().normalize();\r\n\r\n let ref = new THREE.Vector3(0, 1, 0);\r\n if (Math.abs(n.dot(ref)) > 0.99) ref = new THREE.Vector3(0, 0, 1);\r\n const refUp = ref.clone().addScaledVector(n, -ref.dot(n)).normalize();\r\n\r\n let faceUp;\r\n if (sides === 10) {\r\n const H = 1.2;\r\n const pole = face.centroid.y > 0\r\n ? new THREE.Vector3(0, H, 0)\r\n : new THREE.Vector3(0, -H, 0);\r\n const toPole = new THREE.Vector3().subVectors(pole, face.centroid);\r\n toPole.addScaledVector(n, -toPole.dot(n)).normalize();\r\n faceUp = toPole.negate();\r\n } else if (sides !== 6 && face.verts && face.verts.length >= 3) {\r\n let bestDot = -Infinity;\r\n faceUp = refUp;\r\n for (const v of face.verts) {\r\n const dir = new THREE.Vector3().subVectors(v, face.centroid).normalize();\r\n const d = dir.dot(refUp);\r\n if (d > bestDot) {\r\n bestDot = d;\r\n faceUp = dir;\r\n }\r\n }\r\n } else {\r\n faceUp = refUp;\r\n }\r\n\r\n const xAxis = new THREE.Vector3().crossVectors(faceUp, n).normalize();\r\n const m = new THREE.Matrix4().makeBasis(xAxis, faceUp, n);\r\n return new THREE.Quaternion().setFromRotationMatrix(m).conjugate();\r\n}\r\n\r\n/**\r\n * Build a complete die mesh with edges, face labels, and settle metadata.\r\n * Returns a THREE.Mesh with userData: { faces, faceNumbers, numberToFace }\r\n */\r\nexport function buildDieMesh(sides, color, d6Style) {\r\n const geo = createGeometry(sides);\r\n const mat = new THREE.MeshPhongMaterial({\r\n color,\r\n shininess: 80,\r\n specular: 0x333333,\r\n flatShading: sides !== 6,\r\n });\r\n const mesh = new THREE.Mesh(geo, mat);\r\n\r\n // Edge wireframe\r\n const eg = new THREE.EdgesGeometry(geo);\r\n const em = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.2 });\r\n mesh.add(new THREE.LineSegments(eg, em));\r\n\r\n // Face labels (only for standard platonic solid dice)\r\n if (FACE_LABELED.has(sides)) {\r\n const nf = geomFaceCount(sides);\r\n const faces = computeFaces(geo, nf);\r\n mesh.userData.faces = faces;\r\n const ls = LABEL_SIZE[sides] || 0.3;\r\n const useDots = sides === 6 && d6Style === 'dots';\r\n\r\n const faceNumbers = computeFaceNumbers(faces, sides);\r\n mesh.userData.faceNumbers = faceNumbers;\r\n const numberToFace = {};\r\n faceNumbers.forEach((num, idx) => { numberToFace[num] = idx; });\r\n mesh.userData.numberToFace = numberToFace;\r\n\r\n for (let i = 0; i < sides; i++) {\r\n const face = faces[i];\r\n if (!face) continue;\r\n const num = faceNumbers[i];\r\n const tex = useDots ? getDotTexture(num) : getNumTexture(num);\r\n const pg = new THREE.PlaneGeometry(ls, ls);\r\n const pm = new THREE.MeshBasicMaterial({\r\n map: tex,\r\n transparent: true,\r\n depthWrite: false,\r\n side: THREE.DoubleSide,\r\n });\r\n const label = new THREE.Mesh(pg, pm);\r\n label.position.copy(face.centroid).addScaledVector(face.normal, 0.02);\r\n const sq = faceSettleQuat(face, sides);\r\n label.quaternion.copy(sq.conjugate());\r\n mesh.add(label);\r\n }\r\n }\r\n\r\n return mesh;\r\n}\r\n\r\n/**\r\n * Quaternion that rotates a face normal to point at camera (+Z), text upright.\r\n * Looks up the face index from mesh.userData.numberToFace.\r\n */\r\nexport function settleQuat(mesh, result, sides) {\r\n const faces = mesh.userData.faces;\r\n const ntf = mesh.userData.numberToFace;\r\n if (!faces || !ntf) return null;\r\n const fi = ntf[result];\r\n if (fi == null || fi < 0 || fi >= faces.length) return null;\r\n return faceSettleQuat(faces[fi], sides);\r\n}\r\n","import { useRef, useEffect, useState } from 'react';\r\nimport * as THREE from 'three';\r\nimport {\r\n FACE_LABELED,\r\n SETTLE_SECS,\r\n parseColor,\r\n buildDieMesh,\r\n settleQuat,\r\n} from './diceEngine.js';\r\n\r\n/**\r\n * Dice3D -- A React component that renders 3D dice using Three.js.\r\n *\r\n * Props:\r\n * sides (number) - Number of sides (4, 6, 8, 10, 12, 20, or any)\r\n * color (number | string) - Hex color: 0x3b82f6, '#3b82f6', or Tailwind class\r\n * results (number[]) - Array of roll results, one per die\r\n * isRolling (boolean) - Whether dice are currently rolling\r\n * animationMode ('full' | 'quick' | 'none') - Animation style\r\n * rollTrigger (number) - Increment to trigger a new roll render\r\n * d6Style ('numbers' | 'dots') - D6 label style\r\n * height (number) - Container height in px (auto-computed if omitted)\r\n * className (string) - Optional CSS class for the container\r\n * style (object) - Optional inline styles merged onto container\r\n * emptyText (string) - Text shown when no results (default: 'Press Roll to see 3D dice')\r\n */\r\nconst Dice3D = ({\r\n sides,\r\n color = 0x3b82f6,\r\n results = [],\r\n isRolling = false,\r\n animationMode = 'full',\r\n rollTrigger = 0,\r\n d6Style = 'numbers',\r\n height,\r\n className,\r\n style,\r\n emptyText = 'Press Roll to see 3D dice',\r\n}) => {\r\n const mountRef = useRef(null);\r\n const S = useRef({\r\n scene: null, camera: null, renderer: null,\r\n meshes: [], gridPos: [], animId: null,\r\n phase: 'idle', settleStart: 0, settleData: [],\r\n frustumHalf: 2.5,\r\n });\r\n const rollingRef = useRef(isRolling);\r\n const modeRef = useRef(animationMode);\r\n const numberedRef = useRef(FACE_LABELED.has(sides));\r\n rollingRef.current = isRolling;\r\n modeRef.current = animationMode;\r\n numberedRef.current = FACE_LABELED.has(sides);\r\n\r\n const [overlayPos, setOverlayPos] = useState([]);\r\n const hexColor = parseColor(color);\r\n const isNumbered = FACE_LABELED.has(sides);\r\n\r\n // --- Effect 1: Init scene + persistent animation loop ---\r\n useEffect(() => {\r\n const el = mountRef.current;\r\n if (!el) return;\r\n const s = S.current;\r\n\r\n const scene = new THREE.Scene();\r\n const w0 = el.clientWidth;\r\n const h0 = Math.max(el.clientHeight, 1);\r\n const aspect = w0 / h0;\r\n const fh = 2.5;\r\n const camera = new THREE.OrthographicCamera(\r\n -fh * aspect, fh * aspect, fh, -fh, 0.1, 100\r\n );\r\n camera.position.set(0, 0, 10);\r\n const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });\r\n renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\r\n renderer.setSize(el.clientWidth, el.clientHeight);\r\n renderer.setClearColor(0x000000, 0);\r\n el.appendChild(renderer.domElement);\r\n\r\n scene.add(new THREE.AmbientLight(0xffffff, 0.5));\r\n const kl = new THREE.DirectionalLight(0xffffff, 1.0);\r\n kl.position.set(5, 8, 5);\r\n scene.add(kl);\r\n const fl = new THREE.DirectionalLight(0xffffff, 0.3);\r\n fl.position.set(-3, -2, 4);\r\n scene.add(fl);\r\n\r\n s.scene = scene;\r\n s.camera = camera;\r\n s.renderer = renderer;\r\n\r\n function updateOverlay() {\r\n if (s.gridPos.length === 0) return;\r\n camera.updateProjectionMatrix();\r\n setOverlayPos(s.gridPos.map(gp => {\r\n const v = new THREE.Vector3(gp.x, gp.y, 0).project(camera);\r\n return {\r\n left: ((v.x * 0.5 + 0.5) * 100).toFixed(2) + '%',\r\n top: ((-v.y * 0.5 + 0.5) * 100).toFixed(2) + '%',\r\n };\r\n }));\r\n }\r\n s.updateOverlay = updateOverlay;\r\n\r\n const ro = new ResizeObserver(() => {\r\n const w = el.clientWidth;\r\n const h = el.clientHeight;\r\n if (w === 0 || h === 0) return;\r\n const a = w / h;\r\n const fh2 = s.frustumHalf;\r\n camera.left = -fh2 * a;\r\n camera.right = fh2 * a;\r\n camera.top = fh2;\r\n camera.bottom = -fh2;\r\n camera.updateProjectionMatrix();\r\n renderer.setSize(w, h);\r\n updateOverlay();\r\n });\r\n ro.observe(el);\r\n\r\n const loop = (time) => {\r\n s.animId = requestAnimationFrame(loop);\r\n const t = time / 1000;\r\n const meshes = s.meshes;\r\n const mode = modeRef.current;\r\n const numbered = numberedRef.current;\r\n\r\n if (s.phase === 'spinning' && mode !== 'none') {\r\n const spd = mode === 'full' ? 10 : 15;\r\n meshes.forEach((m, i) => {\r\n m.rotation.x += (spd + i * 2) * 0.016;\r\n m.rotation.y += (spd * 0.8 + i) * 0.016;\r\n m.rotation.z += spd * 0.3 * 0.016;\r\n });\r\n } else if (s.phase === 'settling') {\r\n const elapsed = (time - s.settleStart) / 1000;\r\n const p = Math.min(elapsed / SETTLE_SECS, 1);\r\n const e = 1 - Math.pow(1 - p, 3);\r\n meshes.forEach((m, i) => {\r\n const d = s.settleData[i];\r\n if (d) m.quaternion.slerpQuaternions(d.from, d.to, e);\r\n });\r\n if (p >= 1) s.phase = 'idle';\r\n } else if (s.phase === 'idle' && meshes.length > 0) {\r\n meshes.forEach((m, i) => {\r\n if (numbered && s.settleData[i]) {\r\n const w = new THREE.Quaternion().setFromEuler(new THREE.Euler(\r\n Math.sin(t * 0.8 + i) * 0.03,\r\n Math.sin(t * 0.6 + i * 0.5) * 0.05,\r\n 0\r\n ));\r\n m.quaternion.copy(s.settleData[i].to).multiply(w);\r\n } else {\r\n m.rotation.x += 0.004;\r\n m.rotation.y += 0.006;\r\n }\r\n if (s.gridPos[i]) {\r\n m.position.y = s.gridPos[i].y + Math.sin(t * 1.2 + i * 0.7) * 0.06;\r\n }\r\n });\r\n }\r\n\r\n renderer.render(scene, camera);\r\n };\r\n s.animId = requestAnimationFrame(loop);\r\n\r\n return () => {\r\n ro.disconnect();\r\n if (s.animId) cancelAnimationFrame(s.animId);\r\n s.meshes.forEach(m => {\r\n scene.remove(m);\r\n m.traverse(ch => {\r\n if (ch.geometry) ch.geometry.dispose();\r\n if (ch.material) ch.material.dispose();\r\n });\r\n });\r\n renderer.dispose();\r\n if (renderer.domElement.parentNode === el) el.removeChild(renderer.domElement);\r\n s.scene = null; s.camera = null; s.renderer = null;\r\n s.meshes = []; s.gridPos = []; s.animId = null;\r\n };\r\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\r\n\r\n // --- Effect 2: Build meshes when dice config changes ---\r\n useEffect(() => {\r\n const s = S.current;\r\n if (!s.scene) return;\r\n\r\n s.meshes.forEach(m => {\r\n s.scene.remove(m);\r\n m.traverse(ch => {\r\n if (ch.geometry) ch.geometry.dispose();\r\n if (ch.material) ch.material.dispose();\r\n });\r\n });\r\n s.meshes = [];\r\n s.gridPos = [];\r\n s.settleData = [];\r\n s.phase = 'idle';\r\n\r\n const count = results.length;\r\n if (count === 0) { setOverlayPos([]); return; }\r\n\r\n const cols = Math.min(count, 5);\r\n const rows = Math.ceil(count / cols);\r\n const spacing = 2.8;\r\n\r\n for (let i = 0; i < count; i++) {\r\n const mesh = buildDieMesh(sides, hexColor, d6Style);\r\n const row = Math.floor(i / cols);\r\n const inRow = i - row * cols;\r\n const rowItems = row === rows - 1 ? count - row * cols : cols;\r\n const x = (-(rowItems - 1) / 2 + inRow) * spacing;\r\n const y = ((rows - 1) / 2 - row) * spacing;\r\n\r\n mesh.position.set(x, y, 0);\r\n mesh.rotation.set(\r\n Math.random() * Math.PI * 2,\r\n Math.random() * Math.PI * 2,\r\n Math.random() * Math.PI * 2\r\n );\r\n s.scene.add(mesh);\r\n s.meshes.push(mesh);\r\n s.gridPos.push({ x, y });\r\n }\r\n\r\n const el = mountRef.current;\r\n const spanX = Math.min(count, 5) * spacing;\r\n const spanY = rows * spacing;\r\n const asp = el ? el.clientWidth / Math.max(el.clientHeight, 1) : 1;\r\n const margin = 1.5;\r\n const needY = spanY / 2 + margin;\r\n const needX = (spanX / 2 + margin) / asp;\r\n const fh = Math.max(needY, needX, 2.5);\r\n s.frustumHalf = fh;\r\n s.camera.left = -fh * asp;\r\n s.camera.right = fh * asp;\r\n s.camera.top = fh;\r\n s.camera.bottom = -fh;\r\n s.camera.updateProjectionMatrix();\r\n\r\n if (s.updateOverlay) s.updateOverlay();\r\n\r\n if (!rollingRef.current && isNumbered) {\r\n s.phase = 'settling';\r\n s.settleStart = performance.now();\r\n s.settleData = s.meshes.map((m, i) => ({\r\n from: m.quaternion.clone(),\r\n to: settleQuat(m, results[i], sides) || new THREE.Quaternion(),\r\n }));\r\n }\r\n }, [results.length, sides, hexColor, rollTrigger, isNumbered, d6Style]); // eslint-disable-line react-hooks/exhaustive-deps\r\n\r\n // --- Effect 3: Handle rolling state transitions ---\r\n useEffect(() => {\r\n const s = S.current;\r\n if (isRolling) {\r\n s.phase = 'spinning';\r\n s.settleData = [];\r\n } else if (s.phase === 'spinning' && results.length > 0) {\r\n s.phase = 'settling';\r\n s.settleStart = performance.now();\r\n s.settleData = s.meshes.map((m, i) => {\r\n const from = m.quaternion.clone();\r\n let to = null;\r\n if (isNumbered && m.userData.faces) {\r\n to = settleQuat(m, results[i], sides);\r\n }\r\n if (!to) {\r\n to = new THREE.Quaternion().setFromEuler(\r\n new THREE.Euler(Math.random() * 0.3, Math.random() * 0.3, 0)\r\n );\r\n }\r\n return { from, to };\r\n });\r\n }\r\n }, [isRolling, results, isNumbered, sides]);\r\n\r\n // --- Render ---\r\n const rowCount = Math.ceil(results.length / 5) || 1;\r\n const computedH = Math.max(200, rowCount * 130 + 50);\r\n const canvasH = height != null ? height : computedH;\r\n const showOverlay = !isNumbered && !isRolling && results.length > 0;\r\n const fs = results.length > 10 ? '0.875rem' : results.length > 5 ? '1.1rem' : '1.5rem';\r\n\r\n const containerStyle = {\r\n position: 'relative',\r\n overflow: 'hidden',\r\n borderRadius: '0.75rem',\r\n height: typeof canvasH === 'number' ? canvasH + 'px' : canvasH,\r\n ...style,\r\n };\r\n\r\n return (\r\n <div className={className} style={containerStyle}>\r\n <div ref={mountRef} style={{ width: '100%', height: '100%' }} />\r\n\r\n {/* Overlay numbers for non-face-labeled dice */}\r\n {showOverlay && overlayPos.length === results.length &&\r\n results.map((val, i) => (\r\n <div\r\n key={`${rollTrigger}-${i}`}\r\n style={{\r\n position: 'absolute',\r\n pointerEvents: 'none',\r\n left: overlayPos[i].left,\r\n top: overlayPos[i].top,\r\n transform: 'translate(-50%, -50%)',\r\n zIndex: 10,\r\n animation: 'rdice3d-numIn 0.3s ease-out',\r\n }}\r\n >\r\n <span\r\n style={{\r\n fontWeight: 'bold',\r\n color: 'white',\r\n fontVariantNumeric: 'tabular-nums',\r\n fontSize: fs,\r\n textShadow: '0 2px 8px rgba(0,0,0,0.9), 0 0 4px rgba(0,0,0,0.6)',\r\n }}\r\n >\r\n {val}\r\n </span>\r\n </div>\r\n ))}\r\n\r\n {/* Empty state */}\r\n {results.length === 0 && !isRolling && emptyText && (\r\n <div style={{\r\n position: 'absolute',\r\n inset: 0,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n pointerEvents: 'none',\r\n }}>\r\n <p style={{\r\n fontSize: '1.125rem',\r\n fontWeight: 600,\r\n color: '#9ca3af',\r\n margin: 0,\r\n }}>\r\n {emptyText}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <style>{`\r\n @keyframes rdice3d-numIn {\r\n from { opacity: 0; transform: translate(-50%, -50%) scale(0.5); }\r\n to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\r\n }\r\n `}</style>\r\n </div>\r\n );\r\n};\r\n\r\nexport default Dice3D;\r\n"],"names":["FACE_LABELED","LABEL_SIZE","SETTLE_SECS","D6_NUMBERS","TAILWIND_MAP","parseColor","color","texCache","clearTextureCache","tex","getNumTexture","num","sz","c","ctx","text","fs","THREE","DOT_POSITIONS","getDotTexture","key","dots","r","px","py","createD10Geometry","radius","H","h","top","bot","upper","lower","k","aU","aL","verts","push3","v","nk","geo","createGeometry","sides","geomFaceCount","computeFaces","geometry","numFaces","pos","idx","totalTris","tpf","faces","f","centroid","normal","faceVerts","seenIdx","vertCount","t","base","ia","ib","ic","a","b","clusters","vi","n","mid","found","cl","an","bn","unique","isDup","computeFaceNumbers","numbers","target","pairs","used","i","j","p","aA","bA","faceSettleQuat","face","ref","refUp","faceUp","pole","toPole","bestDot","dir","d","xAxis","m","buildDieMesh","d6Style","mat","mesh","eg","em","nf","ls","useDots","faceNumbers","numberToFace","pg","pm","label","sq","settleQuat","result","ntf","fi","Dice3D","results","isRolling","animationMode","rollTrigger","height","className","style","emptyText","mountRef","useRef","S","rollingRef","modeRef","numberedRef","overlayPos","setOverlayPos","useState","hexColor","isNumbered","useEffect","el","s","scene","w0","h0","aspect","fh","camera","renderer","kl","fl","updateOverlay","gp","ro","w","fh2","loop","time","meshes","mode","numbered","spd","elapsed","e","ch","count","cols","rows","spacing","row","inRow","x","y","spanX","spanY","asp","margin","needY","needX","from","to","rowCount","computedH","canvasH","showOverlay","containerStyle","jsxs","jsx","val"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "react-3d-dice",
3
+ "version": "0.1.0",
4
+ "description": "3D dice renderer for React using Three.js. Supports D4, D6, D8, D10, D12, D20 with face labels, settle animations, and dot pips.",
5
+ "type": "module",
6
+ "main": "dist/react-3d-dice.cjs.js",
7
+ "module": "dist/react-3d-dice.es.js",
8
+ "types": "src/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./src/index.d.ts",
12
+ "import": "./dist/react-3d-dice.es.js",
13
+ "require": "./dist/react-3d-dice.cjs.js"
14
+ }
15
+ },
16
+ "sideEffects": false,
17
+ "files": [
18
+ "dist",
19
+ "src/index.d.ts",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "scripts": {
24
+ "build": "vite build",
25
+ "dev": "vite build --watch"
26
+ },
27
+ "peerDependencies": {
28
+ "react": ">=17.0.0",
29
+ "react-dom": ">=17.0.0",
30
+ "three": ">=0.150.0"
31
+ },
32
+ "devDependencies": {
33
+ "@vitejs/plugin-react": "^4.3.4",
34
+ "react": "^19.0.0",
35
+ "react-dom": "^19.0.0",
36
+ "three": "^0.172.0",
37
+ "vite": "^6.1.0"
38
+ },
39
+ "keywords": [
40
+ "react",
41
+ "dice",
42
+ "3d",
43
+ "three.js",
44
+ "d20",
45
+ "d6",
46
+ "tabletop",
47
+ "rpg",
48
+ "dnd",
49
+ "board-game"
50
+ ],
51
+ "author": "Hunter / BosDev",
52
+ "license": "MIT",
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "https://github.com/ChefJulio/react-dice-3d.git"
56
+ },
57
+ "homepage": "https://github.com/ChefJulio/react-dice-3d#readme"
58
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ import { FC, CSSProperties } from 'react';
2
+ import { BufferGeometry, Mesh, Quaternion } from 'three';
3
+
4
+ // --- Component ---
5
+
6
+ export interface Dice3DProps {
7
+ /** Number of sides: 4, 6, 8, 10, 12, 20 (or any for unlabeled) */
8
+ sides: number;
9
+ /** Die color as hex number (0x3b82f6), CSS hex string ('#3b82f6'), or Tailwind class */
10
+ color?: number | string;
11
+ /** Roll results array (one per die rendered) */
12
+ results?: number[];
13
+ /** Whether dice are currently rolling */
14
+ isRolling?: boolean;
15
+ /** Animation style */
16
+ animationMode?: 'full' | 'quick' | 'none';
17
+ /** Increment to trigger a new roll render */
18
+ rollTrigger?: number;
19
+ /** D6 label style */
20
+ d6Style?: 'numbers' | 'dots';
21
+ /** Container height in px (auto-computed from rows if omitted) */
22
+ height?: number;
23
+ /** CSS class for the container */
24
+ className?: string;
25
+ /** Inline styles merged onto container */
26
+ style?: CSSProperties;
27
+ /** Text shown when no results */
28
+ emptyText?: string;
29
+ }
30
+
31
+ declare const Dice3D: FC<Dice3DProps>;
32
+ export default Dice3D;
33
+ export { Dice3D };
34
+
35
+ // --- Engine types ---
36
+
37
+ export interface FaceData {
38
+ centroid: import('three').Vector3;
39
+ normal: import('three').Vector3;
40
+ verts: import('three').Vector3[];
41
+ }
42
+
43
+ export declare const FACE_LABELED: Set<number>;
44
+ export declare const LABEL_SIZE: Record<number, number>;
45
+ export declare const SETTLE_SECS: number;
46
+ export declare const D6_NUMBERS: number[];
47
+
48
+ export declare function parseColor(color: number | string): number;
49
+ export declare function clearTextureCache(): void;
50
+ export declare function getNumTexture(num: number): import('three').CanvasTexture;
51
+ export declare function getDotTexture(num: number): import('three').CanvasTexture;
52
+ export declare function createD10Geometry(radius: number): BufferGeometry;
53
+ export declare function createGeometry(sides: number): BufferGeometry;
54
+ export declare function geomFaceCount(sides: number): number;
55
+ export declare function computeFaces(geometry: BufferGeometry, numFaces: number): FaceData[];
56
+ export declare function computeFaceNumbers(faces: FaceData[], sides: number): number[];
57
+ export declare function faceSettleQuat(face: FaceData, sides: number): Quaternion;
58
+ export declare function buildDieMesh(sides: number, color: number, d6Style?: string): Mesh;
59
+ export declare function settleQuat(mesh: Mesh, result: number, sides: number): Quaternion | null;