@aethervtt/shard-dice 0.0.0-stage → 0.2.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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -2
  3. package/dist/attachments.d.ts +79 -0
  4. package/dist/attachments.js +43 -0
  5. package/dist/audio.d.ts +19 -0
  6. package/dist/audio.js +187 -0
  7. package/dist/builtins.d.ts +25 -0
  8. package/dist/builtins.js +335 -0
  9. package/dist/cells.d.ts +33 -0
  10. package/dist/cells.js +53 -0
  11. package/dist/components.d.ts +14 -0
  12. package/dist/components.js +27 -0
  13. package/dist/definition-asset.d.ts +19 -0
  14. package/dist/definition-asset.js +60 -0
  15. package/dist/definition.d.ts +98 -0
  16. package/dist/definition.js +320 -0
  17. package/dist/dice-worker.d.ts +1 -0
  18. package/dist/dice-worker.js +3 -0
  19. package/dist/effects.d.ts +99 -0
  20. package/dist/effects.js +200 -0
  21. package/dist/entrances.d.ts +48 -0
  22. package/dist/entrances.js +54 -0
  23. package/dist/families.d.ts +17 -0
  24. package/dist/families.js +131 -0
  25. package/dist/glyphs.d.ts +29 -0
  26. package/dist/glyphs.js +270 -0
  27. package/dist/golden.json +17 -0
  28. package/dist/hull.d.ts +32 -0
  29. package/dist/hull.js +154 -0
  30. package/dist/index.d.ts +32 -0
  31. package/dist/index.js +324 -0
  32. package/dist/landing.d.ts +29 -0
  33. package/dist/landing.js +93 -0
  34. package/dist/layout.d.ts +75 -0
  35. package/dist/layout.js +137 -0
  36. package/dist/marks.d.ts +54 -0
  37. package/dist/marks.js +543 -0
  38. package/dist/material.d.ts +80 -0
  39. package/dist/material.js +343 -0
  40. package/dist/math.d.ts +39 -0
  41. package/dist/math.js +136 -0
  42. package/dist/mesh.d.ts +9 -0
  43. package/dist/mesh.js +105 -0
  44. package/dist/methods.d.ts +2 -0
  45. package/dist/methods.js +42 -0
  46. package/dist/numerals.d.ts +9 -0
  47. package/dist/numerals.js +47 -0
  48. package/dist/plugin.d.ts +41 -0
  49. package/dist/plugin.js +176 -0
  50. package/dist/preview.d.ts +32 -0
  51. package/dist/preview.js +135 -0
  52. package/dist/quality.d.ts +12 -0
  53. package/dist/quality.js +18 -0
  54. package/dist/resources.d.ts +85 -0
  55. package/dist/resources.js +191 -0
  56. package/dist/roll.d.ts +68 -0
  57. package/dist/roll.js +97 -0
  58. package/dist/settle.d.ts +53 -0
  59. package/dist/settle.js +96 -0
  60. package/dist/skin.d.ts +64 -0
  61. package/dist/skin.js +313 -0
  62. package/dist/sound.d.ts +33 -0
  63. package/dist/sound.js +296 -0
  64. package/dist/studio.d.ts +3 -0
  65. package/dist/studio.js +48 -0
  66. package/dist/table.d.ts +227 -0
  67. package/dist/table.js +1818 -0
  68. package/dist/test-worker.d.ts +1 -0
  69. package/dist/test-worker.js +12 -0
  70. package/dist/track.d.ts +70 -0
  71. package/dist/track.js +304 -0
  72. package/dist/tray.d.ts +2 -0
  73. package/dist/tray.js +43 -0
  74. package/dist/variants.d.ts +9 -0
  75. package/dist/variants.js +46 -0
  76. package/dist/windows.d.ts +25 -0
  77. package/dist/windows.js +77 -0
  78. package/dist/worker.d.ts +6 -0
  79. package/dist/worker.js +9 -0
  80. package/package.json +47 -4
package/dist/hull.js ADDED
@@ -0,0 +1,154 @@
1
+ import {
2
+ add,
3
+ cross,
4
+ dot,
5
+ EPSILON,
6
+ length,
7
+ normalize,
8
+ qConj,
9
+ qFromBasis,
10
+ qKey,
11
+ qMul,
12
+ qRotate,
13
+ round,
14
+ scale,
15
+ stableTangent,
16
+ sub
17
+ } from "./math.js";
18
+ function pointsOf(flat) {
19
+ const out = [];
20
+ for (let i = 0; i + 2 < flat.length; i += 3) out.push([flat[i], flat[i + 1], flat[i + 2]]);
21
+ return out;
22
+ }
23
+ const compareVector = (a, b) => a[1] - b[1] || a[2] - b[2] || a[0] - b[0];
24
+ function convexFaces(points) {
25
+ const faces = /* @__PURE__ */ new Map();
26
+ const n = points.length;
27
+ for (let a = 0; a < n - 2; a++) {
28
+ for (let b = a + 1; b < n - 1; b++) {
29
+ for (let c = b + 1; c < n; c++) {
30
+ let normal = cross(sub(points[b], points[a]), sub(points[c], points[a]));
31
+ if (dot(normal, normal) < 1e-10) continue;
32
+ normal = normalize(normal);
33
+ const d = dot(normal, points[a]);
34
+ let positive = false;
35
+ let negative = false;
36
+ for (const p of points) {
37
+ const side = dot(normal, p) - d;
38
+ if (side > EPSILON) positive = true;
39
+ else if (side < -EPSILON) negative = true;
40
+ if (positive && negative) break;
41
+ }
42
+ if (positive && negative) continue;
43
+ if (positive) normal = scale(normal, -1);
44
+ const plane = dot(normal, points[a]);
45
+ const members = [];
46
+ for (let i = 0; i < n; i++) {
47
+ if (Math.abs(dot(normal, points[i]) - plane) < EPSILON * 3) members.push(i);
48
+ }
49
+ const key = members.join(":");
50
+ if (faces.has(key)) continue;
51
+ let center = [0, 0, 0];
52
+ for (const i of members) center = add(center, points[i]);
53
+ center = scale(center, 1 / members.length);
54
+ faces.set(key, { vertices: orderAround(members, points, normal, center), normal, center });
55
+ }
56
+ }
57
+ }
58
+ return [...faces.values()].sort((l, r) => compareVector(l.normal, r.normal));
59
+ }
60
+ function orderAround(members, points, normal, center) {
61
+ const t = stableTangent(normal);
62
+ const b = cross(normal, t);
63
+ const angle = (i) => {
64
+ const d = sub(points[i], center);
65
+ return Math.atan2(dot(d, b), dot(d, t));
66
+ };
67
+ return [...members].sort((l, r) => angle(l) - angle(r));
68
+ }
69
+ function tangentPolytope(normals) {
70
+ const triangles = convexFaces(normals);
71
+ const points = [];
72
+ const cells = normals.map(() => []);
73
+ for (const tri of triangles) {
74
+ const [a, b, c] = tri.vertices;
75
+ const na = normals[a];
76
+ const nb = normals[b];
77
+ const nc = normals[c];
78
+ const det = dot(na, cross(nb, nc));
79
+ const p = scale(add(add(cross(nb, nc), cross(nc, na)), cross(na, nb)), 1 / det);
80
+ const index = points.push(p) - 1;
81
+ for (const i of tri.vertices) cells[i].push(index);
82
+ }
83
+ const faces = normals.map((normal, i) => {
84
+ let center = [0, 0, 0];
85
+ for (const v of cells[i]) center = add(center, points[v]);
86
+ center = scale(center, 1 / cells[i].length);
87
+ return { vertices: orderAround(cells[i], points, normal, center), normal, center };
88
+ });
89
+ return { points, faces };
90
+ }
91
+ function hasPoint(points, p) {
92
+ for (const q of points) {
93
+ const dx = q[0] - p[0];
94
+ const dy = q[1] - p[1];
95
+ const dz = q[2] - p[2];
96
+ if (dx * dx + dy * dy + dz * dz < EPSILON * EPSILON * 9) return true;
97
+ }
98
+ return false;
99
+ }
100
+ function isSymmetry(points, q) {
101
+ for (const p of points) if (!hasPoint(points, qRotate(q, p))) return false;
102
+ return true;
103
+ }
104
+ function basisOf(a, b, c) {
105
+ const x = normalize(sub(b, a));
106
+ const ac = sub(c, a);
107
+ const yRaw = sub(ac, scale(x, dot(ac, x)));
108
+ if (length(yRaw) < 1e-6) return void 0;
109
+ const y = normalize(yRaw);
110
+ return [x, y, cross(x, y)];
111
+ }
112
+ function symmetryGroup(points) {
113
+ const n = points.length;
114
+ let anchor;
115
+ for (let b = 1; b < n && !anchor; b++) {
116
+ for (let c = b + 1; c < n && !anchor; c++) {
117
+ if (length(cross(sub(points[b], points[0]), sub(points[c], points[0]))) > 1e-4)
118
+ anchor = [0, b, c];
119
+ }
120
+ }
121
+ if (!anchor) return [];
122
+ const [p0, p1, p2] = anchor.map((i) => points[i]);
123
+ const d01 = length(sub(p1, p0));
124
+ const d02 = length(sub(p2, p0));
125
+ const d12 = length(sub(p2, p1));
126
+ const source = basisOf(p0, p1, p2);
127
+ const fromSource = qConj(qFromBasis(...source));
128
+ const found = /* @__PURE__ */ new Map();
129
+ for (let a = 0; a < n; a++) {
130
+ for (let b = 0; b < n; b++) {
131
+ if (b === a || Math.abs(length(sub(points[b], points[a])) - d01) > EPSILON * 10) continue;
132
+ for (let c = 0; c < n; c++) {
133
+ if (c === a || c === b) continue;
134
+ if (Math.abs(length(sub(points[c], points[a])) - d02) > EPSILON * 10) continue;
135
+ if (Math.abs(length(sub(points[c], points[b])) - d12) > EPSILON * 10) continue;
136
+ const target = basisOf(points[a], points[b], points[c]);
137
+ if (!target) continue;
138
+ const q = qMul(qFromBasis(...target), fromSource);
139
+ const moved = qRotate(q, p0);
140
+ if (length(sub(moved, points[a])) > EPSILON * 10) continue;
141
+ if (!isSymmetry(points, q)) continue;
142
+ found.set(qKey(q), q.map(round));
143
+ }
144
+ }
145
+ }
146
+ return [...found.entries()].sort(([l], [r]) => l.localeCompare(r)).map(([, q]) => q);
147
+ }
148
+ export {
149
+ convexFaces,
150
+ isSymmetry,
151
+ pointsOf,
152
+ symmetryGroup,
153
+ tangentPolytope
154
+ };
@@ -0,0 +1,32 @@
1
+ export { DICE_AUDIO_BUS } from './audio.js';
2
+ export { allDiceAttachments, DICE_BUDGETS, type DiceAttachmentContext, type DiceAttachmentDef, type DiceSceneContext, defineDiceAttachment, findDiceAttachment, MAX_ATTACHMENTS, setDiceBudgets, } from './attachments.js';
3
+ export { BUILTIN_DICE, D4, D6, D8, D10, D10_TENS, D12, D20, D100_BALL, DIE_KINDS, type DieKind, KIND_DEFINITIONS, kindSides, percentileValues, registerBuiltinDice, } from './builtins.js';
4
+ export { CELL_PAD, type CellLayout, cellLayout, type FaceCell, MARK_RANGE } from './cells.js';
5
+ export { DiceDie, DiceRollRequest } from './components.js';
6
+ export { allDice, type DieDefinition, type DieGeometry, defineDie, dieGeometry, dieHash, findDie, frameOf, labelOf, physicalScale, requireDie, type ValueFrame, validateDieDefinition, } from './definition.js';
7
+ export { DieDefinitionAsset, type DieDefinitionAssetValue, dieDefinitionOf, } from './definition-asset.js';
8
+ export { type DiceCondition, type DiceEffect, DiceEffectRecipe, type DiceEffectRecipeValue, diceEffectRecipe, type MatchedRecipe, matchRecipes, RECIPE_LIMITS, recipeProblems, } from './effects.js';
9
+ export { allDiceEntrances, type DiceEntranceContext, type DiceEntranceDef, defineDiceEntrance, findDiceEntrance, MAX_ENTRANCES, } from './entrances.js';
10
+ export { BUILTIN_FAMILIES, GLASS, METAL, RESIN, SOLID } from './families.js';
11
+ export { allDiceGlyphs, BUILTIN_GLYPHS, type DiceGlyph, type DiceGlyphDef, defineDiceGlyph, findDiceGlyph, parseSvgPath, registerBuiltinGlyphs, } from './glyphs.js';
12
+ export { landingCorrection, markAngle, naturalValue, restingHeight, restRotation, topAlignment, } from './landing.js';
13
+ export { ambiguousLabel, defaultLayoutFor, FaceLayout, type FaceLayoutValue, type FaceMark, layoutProblems, NUMBERS_LAYOUT, PIPS_LAYOUT, type ResolvedMark, resolveMark, } from './layout.js';
14
+ export { type BakeMarksOptions, bakeMarks, clearMarkCache, MARK_MARGIN, type MarkAtlas, markAtlasKey, markPlacements, type Placement as MarkPlacement, } from './marks.js';
15
+ export { allDiceFamilies, DICE_FIELDS, DICE_SHADERS, type DiceFamily, type DiceFamilyOptions, defineDiceFamily, findDiceFamily, isDiceMaterial, } from './material.js';
16
+ export { dieMesh, floorQuad } from './mesh.js';
17
+ export { diceMethods } from './methods.js';
18
+ export { NUMERAL_HEIGHT } from './numerals.js';
19
+ export { type DicePluginOptions, dicePlugin, TOP_DOWN } from './plugin.js';
20
+ export { type DiceThumbnail, type DiceThumbnailOptions, renderDiceThumbnail } from './preview.js';
21
+ export { type DiceQuality, type DiceQualityChoice, resolveDiceQuality } from './quality.js';
22
+ export { type DiceLookKey, type DiceResourceEntry, DiceResources, RELEASE_AFTER_MS, } from './resources.js';
23
+ export { type DiceQualityPreference, type DiceRoll, type DiceRollDie, expandRoll, type PhysicalDie, presentationScale, rollTrackRequest, viewportTray, } from './roll.js';
24
+ export { BALL_DAMPING, DICE_CLEANUP_STEP, DICE_GROUPS, DICE_SETTLE_RULE, type DiceSettleParams, type DiceTray, diceSettle, outsideTray, restingFlat, } from './settle.js';
25
+ export { BUILTIN_SKINS, DICE_SKINS, DiceSkin, type DiceSkinValue, type DiceSkinVariant, diceSkin, IMPACT_SOUNDS, type ImpactSound, MAX_SKIN_EFFECTS, type SkinContext, skinVariant, validateDiceSkin, } from './skin.js';
26
+ export { ACCENT_CUES, type AccentCue, accentSamples, DiceSoundBank, IMPACT_LAYERS, IMPACT_VARIATIONS, impactLayer, impactSamples, impactStrength, wavBytes, } from './sound.js';
27
+ export { studioEnvironment } from './studio.js';
28
+ export { ANIMATED_DEMAND, type DiceOutcome, type DicePhase, type DicePlayOptions, DiceTable, type DiceTableOptions, DiceTableState, ENTRANCE_DEMAND, ENTRANCE_WAIT_MS, PRESENTATION_DEMAND, PREVIEW_TRAY, } from './table.js';
29
+ export { DICE_CONTACTS, DICE_GRAVITY, DICE_MAX_STEPS, DICE_STEP, type DiceTrackDie, type DiceTrackRequest, diceSettleParams, diceTrackScene, LAUNCH_EDGES, type LaunchEdge, laneLayout, launchEdgeOf, MAX_DICE, PLACED_DROP_STEPS, placeDie, placedFrom, placementSpots, seedOf, type UnlandedReason, unlandedDice, } from './track.js';
30
+ export { DiceTray as DiceTrayMaterial, TRAY_SHADERS } from './tray.js';
31
+ export { type DiceVariantEntry, diceVariants } from './variants.js';
32
+ export { type DiceWindow, spawnDiceWindow } from './windows.js';
package/dist/index.js ADDED
@@ -0,0 +1,324 @@
1
+ import { DICE_AUDIO_BUS } from "./audio.js";
2
+ import {
3
+ allDiceAttachments,
4
+ DICE_BUDGETS,
5
+ defineDiceAttachment,
6
+ findDiceAttachment,
7
+ MAX_ATTACHMENTS,
8
+ setDiceBudgets
9
+ } from "./attachments.js";
10
+ import {
11
+ BUILTIN_DICE,
12
+ D4,
13
+ D6,
14
+ D8,
15
+ D10,
16
+ D10_TENS,
17
+ D12,
18
+ D20,
19
+ D100_BALL,
20
+ DIE_KINDS,
21
+ KIND_DEFINITIONS,
22
+ kindSides,
23
+ percentileValues,
24
+ registerBuiltinDice
25
+ } from "./builtins.js";
26
+ import { CELL_PAD, cellLayout, MARK_RANGE } from "./cells.js";
27
+ import { DiceDie, DiceRollRequest } from "./components.js";
28
+ import {
29
+ allDice,
30
+ defineDie,
31
+ dieGeometry,
32
+ dieHash,
33
+ findDie,
34
+ frameOf,
35
+ labelOf,
36
+ physicalScale,
37
+ requireDie,
38
+ validateDieDefinition
39
+ } from "./definition.js";
40
+ import {
41
+ DieDefinitionAsset,
42
+ dieDefinitionOf
43
+ } from "./definition-asset.js";
44
+ import {
45
+ DiceEffectRecipe,
46
+ diceEffectRecipe,
47
+ matchRecipes,
48
+ RECIPE_LIMITS,
49
+ recipeProblems
50
+ } from "./effects.js";
51
+ import {
52
+ allDiceEntrances,
53
+ defineDiceEntrance,
54
+ findDiceEntrance,
55
+ MAX_ENTRANCES
56
+ } from "./entrances.js";
57
+ import { BUILTIN_FAMILIES, GLASS, METAL, RESIN, SOLID } from "./families.js";
58
+ import {
59
+ allDiceGlyphs,
60
+ BUILTIN_GLYPHS,
61
+ defineDiceGlyph,
62
+ findDiceGlyph,
63
+ parseSvgPath,
64
+ registerBuiltinGlyphs
65
+ } from "./glyphs.js";
66
+ import {
67
+ landingCorrection,
68
+ markAngle,
69
+ naturalValue,
70
+ restingHeight,
71
+ restRotation,
72
+ topAlignment
73
+ } from "./landing.js";
74
+ import {
75
+ ambiguousLabel,
76
+ defaultLayoutFor,
77
+ FaceLayout,
78
+ layoutProblems,
79
+ NUMBERS_LAYOUT,
80
+ PIPS_LAYOUT,
81
+ resolveMark
82
+ } from "./layout.js";
83
+ import {
84
+ bakeMarks,
85
+ clearMarkCache,
86
+ MARK_MARGIN,
87
+ markAtlasKey,
88
+ markPlacements
89
+ } from "./marks.js";
90
+ import {
91
+ allDiceFamilies,
92
+ DICE_FIELDS,
93
+ DICE_SHADERS,
94
+ defineDiceFamily,
95
+ findDiceFamily,
96
+ isDiceMaterial
97
+ } from "./material.js";
98
+ import { dieMesh, floorQuad } from "./mesh.js";
99
+ import { diceMethods } from "./methods.js";
100
+ import { NUMERAL_HEIGHT } from "./numerals.js";
101
+ import { dicePlugin, TOP_DOWN } from "./plugin.js";
102
+ import { renderDiceThumbnail } from "./preview.js";
103
+ import { resolveDiceQuality } from "./quality.js";
104
+ import {
105
+ DiceResources,
106
+ RELEASE_AFTER_MS
107
+ } from "./resources.js";
108
+ import {
109
+ expandRoll,
110
+ presentationScale,
111
+ rollTrackRequest,
112
+ viewportTray
113
+ } from "./roll.js";
114
+ import {
115
+ BALL_DAMPING,
116
+ DICE_CLEANUP_STEP,
117
+ DICE_GROUPS,
118
+ DICE_SETTLE_RULE,
119
+ diceSettle,
120
+ outsideTray,
121
+ restingFlat
122
+ } from "./settle.js";
123
+ import {
124
+ BUILTIN_SKINS,
125
+ DICE_SKINS,
126
+ DiceSkin,
127
+ diceSkin,
128
+ IMPACT_SOUNDS,
129
+ MAX_SKIN_EFFECTS,
130
+ skinVariant,
131
+ validateDiceSkin
132
+ } from "./skin.js";
133
+ import {
134
+ ACCENT_CUES,
135
+ accentSamples,
136
+ DiceSoundBank,
137
+ IMPACT_LAYERS,
138
+ IMPACT_VARIATIONS,
139
+ impactLayer,
140
+ impactSamples,
141
+ impactStrength,
142
+ wavBytes
143
+ } from "./sound.js";
144
+ import { studioEnvironment } from "./studio.js";
145
+ import {
146
+ ANIMATED_DEMAND,
147
+ DiceTable,
148
+ DiceTableState,
149
+ ENTRANCE_DEMAND,
150
+ ENTRANCE_WAIT_MS,
151
+ PRESENTATION_DEMAND,
152
+ PREVIEW_TRAY
153
+ } from "./table.js";
154
+ import {
155
+ DICE_CONTACTS,
156
+ DICE_GRAVITY,
157
+ DICE_MAX_STEPS,
158
+ DICE_STEP,
159
+ diceSettleParams,
160
+ diceTrackScene,
161
+ LAUNCH_EDGES,
162
+ laneLayout,
163
+ launchEdgeOf,
164
+ MAX_DICE,
165
+ PLACED_DROP_STEPS,
166
+ placeDie,
167
+ placedFrom,
168
+ placementSpots,
169
+ seedOf,
170
+ unlandedDice
171
+ } from "./track.js";
172
+ import { DiceTray, TRAY_SHADERS } from "./tray.js";
173
+ import { diceVariants } from "./variants.js";
174
+ import { spawnDiceWindow } from "./windows.js";
175
+ export {
176
+ ACCENT_CUES,
177
+ ANIMATED_DEMAND,
178
+ BALL_DAMPING,
179
+ BUILTIN_DICE,
180
+ BUILTIN_FAMILIES,
181
+ BUILTIN_GLYPHS,
182
+ BUILTIN_SKINS,
183
+ CELL_PAD,
184
+ D10,
185
+ D100_BALL,
186
+ D10_TENS,
187
+ D12,
188
+ D20,
189
+ D4,
190
+ D6,
191
+ D8,
192
+ DICE_AUDIO_BUS,
193
+ DICE_BUDGETS,
194
+ DICE_CLEANUP_STEP,
195
+ DICE_CONTACTS,
196
+ DICE_FIELDS,
197
+ DICE_GRAVITY,
198
+ DICE_GROUPS,
199
+ DICE_MAX_STEPS,
200
+ DICE_SETTLE_RULE,
201
+ DICE_SHADERS,
202
+ DICE_SKINS,
203
+ DICE_STEP,
204
+ DIE_KINDS,
205
+ DiceDie,
206
+ DiceEffectRecipe,
207
+ DiceResources,
208
+ DiceRollRequest,
209
+ DiceSkin,
210
+ DiceSoundBank,
211
+ DiceTable,
212
+ DiceTableState,
213
+ DiceTray as DiceTrayMaterial,
214
+ DieDefinitionAsset,
215
+ ENTRANCE_DEMAND,
216
+ ENTRANCE_WAIT_MS,
217
+ FaceLayout,
218
+ GLASS,
219
+ IMPACT_LAYERS,
220
+ IMPACT_SOUNDS,
221
+ IMPACT_VARIATIONS,
222
+ KIND_DEFINITIONS,
223
+ LAUNCH_EDGES,
224
+ MARK_MARGIN,
225
+ MARK_RANGE,
226
+ MAX_ATTACHMENTS,
227
+ MAX_DICE,
228
+ MAX_ENTRANCES,
229
+ MAX_SKIN_EFFECTS,
230
+ METAL,
231
+ NUMBERS_LAYOUT,
232
+ NUMERAL_HEIGHT,
233
+ PIPS_LAYOUT,
234
+ PLACED_DROP_STEPS,
235
+ PRESENTATION_DEMAND,
236
+ PREVIEW_TRAY,
237
+ RECIPE_LIMITS,
238
+ RELEASE_AFTER_MS,
239
+ RESIN,
240
+ SOLID,
241
+ TOP_DOWN,
242
+ TRAY_SHADERS,
243
+ accentSamples,
244
+ allDice,
245
+ allDiceAttachments,
246
+ allDiceEntrances,
247
+ allDiceFamilies,
248
+ allDiceGlyphs,
249
+ ambiguousLabel,
250
+ bakeMarks,
251
+ cellLayout,
252
+ clearMarkCache,
253
+ defaultLayoutFor,
254
+ defineDiceAttachment,
255
+ defineDiceEntrance,
256
+ defineDiceFamily,
257
+ defineDiceGlyph,
258
+ defineDie,
259
+ diceEffectRecipe,
260
+ diceMethods,
261
+ dicePlugin,
262
+ diceSettle,
263
+ diceSettleParams,
264
+ diceSkin,
265
+ diceTrackScene,
266
+ diceVariants,
267
+ dieDefinitionOf,
268
+ dieGeometry,
269
+ dieHash,
270
+ dieMesh,
271
+ expandRoll,
272
+ findDiceAttachment,
273
+ findDiceEntrance,
274
+ findDiceFamily,
275
+ findDiceGlyph,
276
+ findDie,
277
+ floorQuad,
278
+ frameOf,
279
+ impactLayer,
280
+ impactSamples,
281
+ impactStrength,
282
+ isDiceMaterial,
283
+ kindSides,
284
+ labelOf,
285
+ landingCorrection,
286
+ laneLayout,
287
+ launchEdgeOf,
288
+ layoutProblems,
289
+ markAngle,
290
+ markAtlasKey,
291
+ markPlacements,
292
+ matchRecipes,
293
+ naturalValue,
294
+ outsideTray,
295
+ parseSvgPath,
296
+ percentileValues,
297
+ physicalScale,
298
+ placeDie,
299
+ placedFrom,
300
+ placementSpots,
301
+ presentationScale,
302
+ recipeProblems,
303
+ registerBuiltinDice,
304
+ registerBuiltinGlyphs,
305
+ renderDiceThumbnail,
306
+ requireDie,
307
+ resolveDiceQuality,
308
+ resolveMark,
309
+ restRotation,
310
+ restingFlat,
311
+ restingHeight,
312
+ rollTrackRequest,
313
+ seedOf,
314
+ setDiceBudgets,
315
+ skinVariant,
316
+ spawnDiceWindow,
317
+ studioEnvironment,
318
+ topAlignment,
319
+ unlandedDice,
320
+ validateDiceSkin,
321
+ validateDieDefinition,
322
+ viewportTray,
323
+ wavBytes
324
+ };
@@ -0,0 +1,29 @@
1
+ import { type DieDefinition, type DieGeometry } from './definition.js';
2
+ import { type Q4, type V3 } from './math.js';
3
+ /** Screen-up as a direction on the floor (the camera's up, flattened). */
4
+ export declare function floorUp(screenUp: ArrayLike<number>): V3;
5
+ /** The value on top for a body rotation: the one whose direction points most nearly up. */
6
+ export declare function naturalValue(die: DieDefinition | DieGeometry, rotation: ArrayLike<number>): number;
7
+ /** How nearly the top value's direction points up: 1 when it's exactly vertical. */
8
+ export declare function topAlignment(die: DieDefinition | DieGeometry, rotation: ArrayLike<number>): number;
9
+ /**
10
+ * The angle (radians) between a value's mark-up on screen and screen-up, both flattened onto the
11
+ * floor, for a die shown at `rotation`.
12
+ */
13
+ export declare function markAngle(die: DieDefinition | DieGeometry, rotation: ArrayLike<number>, value: number, screenUp?: ArrayLike<number>): number;
14
+ /**
15
+ * The correction for a die that came to rest at `finalRotation`: a symmetry C of its collider,
16
+ * post-multiplied onto every sampled rotation (shown = rotation · C), that puts `target` where the
17
+ * natural value is. Among the symmetries that do, it's the one whose mark reads closest to
18
+ * screen-up. A ball's group is every rotation, so its target ends exactly up and upright. Throws
19
+ * `dice/invalid-value` for a value the die can't show.
20
+ */
21
+ export declare function landingCorrection(die: DieDefinition | DieGeometry, finalRotation: ArrayLike<number>, target: number, screenUp?: ArrayLike<number>): Q4;
22
+ /**
23
+ * A rotation that rests `target` on top with its mark exactly toward screen-up: reduced motion,
24
+ * placed dice and thumbnails. The face opposite rests on the floor (a d4 rests on the face
25
+ * opposite its vertex).
26
+ */
27
+ export declare function restRotation(die: DieDefinition | DieGeometry, target: number, screenUp?: ArrayLike<number>): Q4;
28
+ /** The height of the die's center above the floor at `rotation`: its lowest point touches it. */
29
+ export declare function restingHeight(die: DieDefinition | DieGeometry, rotation: ArrayLike<number>, scale: number): number;
@@ -0,0 +1,93 @@
1
+ import { dieGeometry, frameOf } from "./definition.js";
2
+ import {
3
+ angleBetween,
4
+ dot,
5
+ EPSILON,
6
+ length,
7
+ normalize,
8
+ qConj,
9
+ qFromFrames,
10
+ qMul,
11
+ qNormalize,
12
+ qRotate,
13
+ sub
14
+ } from "./math.js";
15
+ const UP = [0, 1, 0];
16
+ const geometryOf = (die) => "frames" in die ? die : dieGeometry(die);
17
+ const quat = (q) => qNormalize([q[0], q[1], q[2], q[3]]);
18
+ function floorUp(screenUp) {
19
+ const flat = [screenUp[0], 0, screenUp[2]];
20
+ const l = length(flat);
21
+ return l > 1e-6 ? [flat[0] / l, 0, flat[2] / l] : [0, 0, -1];
22
+ }
23
+ function naturalValue(die, rotation) {
24
+ const g = geometryOf(die);
25
+ const q = quat(rotation);
26
+ let best = g.frames[0].value;
27
+ let height = Number.NEGATIVE_INFINITY;
28
+ for (const frame of g.frames) {
29
+ const y = qRotate(q, frame.normal)[1];
30
+ if (y > height) {
31
+ height = y;
32
+ best = frame.value;
33
+ }
34
+ }
35
+ return best;
36
+ }
37
+ function topAlignment(die, rotation) {
38
+ const g = geometryOf(die);
39
+ if (g.definition.collider === "ball") return 1;
40
+ const q = quat(rotation);
41
+ let height = Number.NEGATIVE_INFINITY;
42
+ for (const frame of g.frames) height = Math.max(height, qRotate(q, frame.normal)[1]);
43
+ return height;
44
+ }
45
+ function markAngle(die, rotation, value, screenUp = [0, 0, -1]) {
46
+ const g = geometryOf(die);
47
+ const b = qRotate(quat(rotation), frameOf(g, value).bitangent);
48
+ return angleBetween(normalize([b[0], 0, b[2]]), floorUp(screenUp));
49
+ }
50
+ function landingCorrection(die, finalRotation, target, screenUp = [0, 0, -1]) {
51
+ const g = geometryOf(die);
52
+ const frame = frameOf(g, target);
53
+ const r = quat(finalRotation);
54
+ const up = floorUp(screenUp);
55
+ if (g.definition.collider === "ball") {
56
+ return qNormalize(qMul(qConj(r), qFromFrames(frame.normal, frame.bitangent, UP, up)));
57
+ }
58
+ const natural = frameOf(g, naturalValue(g, r));
59
+ let best;
60
+ let score = Number.NEGATIVE_INFINITY;
61
+ for (const c of g.symmetries) {
62
+ if (length(sub(qRotate(c, frame.normal), natural.normal)) > EPSILON * 10) continue;
63
+ const b = qRotate(r, qRotate(c, frame.bitangent));
64
+ const s = dot(normalize([b[0], 0, b[2]]), up);
65
+ if (s > score + 1e-9) {
66
+ score = s;
67
+ best = c;
68
+ }
69
+ }
70
+ return best ?? [0, 0, 0, 1];
71
+ }
72
+ function restRotation(die, target, screenUp = [0, 0, -1]) {
73
+ const g = geometryOf(die);
74
+ const frame = frameOf(g, target);
75
+ return qFromFrames(frame.normal, frame.bitangent, UP, floorUp(screenUp));
76
+ }
77
+ function restingHeight(die, rotation, scale) {
78
+ const g = geometryOf(die);
79
+ if (g.definition.collider === "ball") return scale;
80
+ const q = quat(rotation);
81
+ let low = 0;
82
+ for (const p of g.points) low = Math.min(low, qRotate(q, p)[1]);
83
+ return -low * scale;
84
+ }
85
+ export {
86
+ floorUp,
87
+ landingCorrection,
88
+ markAngle,
89
+ naturalValue,
90
+ restRotation,
91
+ restingHeight,
92
+ topAlignment
93
+ };