@12ui/design 0.2.70 → 0.2.72
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/README.md +5 -0
- package/SKILL.md +1 -1
- package/dist/cli-capabilities.d.ts +9 -0
- package/dist/cli-capabilities.d.ts.map +1 -1
- package/dist/cli-capabilities.js +10 -0
- package/dist/cli-capabilities.js.map +1 -1
- package/dist/cli-conversion-recovery-advice.d.ts +35 -0
- package/dist/cli-conversion-recovery-advice.d.ts.map +1 -0
- package/dist/cli-conversion-recovery-advice.js +72 -0
- package/dist/cli-conversion-recovery-advice.js.map +1 -0
- package/dist/cli-convert-command.d.ts.map +1 -1
- package/dist/cli-convert-command.js +153 -35
- package/dist/cli-convert-command.js.map +1 -1
- package/dist/cli-help/index.d.ts +93 -0
- package/dist/cli-help/index.d.ts.map +1 -1
- package/dist/cli-help/index.js +2 -0
- package/dist/cli-help/index.js.map +1 -1
- package/dist/cli-help/scene.d.ts +96 -0
- package/dist/cli-help/scene.d.ts.map +1 -0
- package/dist/cli-help/scene.js +162 -0
- package/dist/cli-help/scene.js.map +1 -0
- package/dist/cli-run.d.ts.map +1 -1
- package/dist/cli-run.js +6 -0
- package/dist/cli-run.js.map +1 -1
- package/dist/cli-scene-command.d.ts +3 -0
- package/dist/cli-scene-command.d.ts.map +1 -0
- package/dist/cli-scene-command.js +292 -0
- package/dist/cli-scene-command.js.map +1 -0
- package/dist/cli-usage.d.ts +8 -0
- package/dist/cli-usage.d.ts.map +1 -1
- package/dist/cli-usage.js +26 -1
- package/dist/cli-usage.js.map +1 -1
- package/dist/conversion-client.d.ts +11 -0
- package/dist/conversion-client.d.ts.map +1 -1
- package/dist/conversion-client.js +11 -1
- package/dist/conversion-client.js.map +1 -1
- package/dist/legacy-skill-catalog.d.ts.map +1 -1
- package/dist/legacy-skill-catalog.js +11 -0
- package/dist/legacy-skill-catalog.js.map +1 -1
- package/dist/prototype-browsers.d.ts +8 -2
- package/dist/prototype-browsers.d.ts.map +1 -1
- package/dist/prototype-browsers.js +8 -3
- package/dist/prototype-browsers.js.map +1 -1
- package/dist/prototype-process.js +1 -1
- package/dist/prototype-process.js.map +1 -1
- package/dist/run-journal-commands.d.ts.map +1 -1
- package/dist/run-journal-commands.js +6 -0
- package/dist/run-journal-commands.js.map +1 -1
- package/dist/scene/camera-model.d.ts +48 -0
- package/dist/scene/camera-model.d.ts.map +1 -0
- package/dist/scene/camera-model.js +89 -0
- package/dist/scene/camera-model.js.map +1 -0
- package/dist/scene/camera-solve.d.ts +58 -0
- package/dist/scene/camera-solve.d.ts.map +1 -0
- package/dist/scene/camera-solve.js +288 -0
- package/dist/scene/camera-solve.js.map +1 -0
- package/dist/scene/capture-plan.d.ts +28 -0
- package/dist/scene/capture-plan.d.ts.map +1 -0
- package/dist/scene/capture-plan.js +79 -0
- package/dist/scene/capture-plan.js.map +1 -0
- package/dist/scene/capture.d.ts +21 -0
- package/dist/scene/capture.d.ts.map +1 -0
- package/dist/scene/capture.js +118 -0
- package/dist/scene/capture.js.map +1 -0
- package/dist/scene/image-colors.d.ts +25 -0
- package/dist/scene/image-colors.d.ts.map +1 -0
- package/dist/scene/image-colors.js +110 -0
- package/dist/scene/image-colors.js.map +1 -0
- package/dist/scene/image-rectify.d.ts +25 -0
- package/dist/scene/image-rectify.d.ts.map +1 -0
- package/dist/scene/image-rectify.js +82 -0
- package/dist/scene/image-rectify.js.map +1 -0
- package/dist/scene/image-sheet.d.ts +19 -0
- package/dist/scene/image-sheet.d.ts.map +1 -0
- package/dist/scene/image-sheet.js +118 -0
- package/dist/scene/image-sheet.js.map +1 -0
- package/dist/scene/least-squares.d.ts +31 -0
- package/dist/scene/least-squares.d.ts.map +1 -0
- package/dist/scene/least-squares.js +173 -0
- package/dist/scene/least-squares.js.map +1 -0
- package/dist/scene/numbers.d.ts +17 -0
- package/dist/scene/numbers.d.ts.map +1 -0
- package/dist/scene/numbers.js +39 -0
- package/dist/scene/numbers.js.map +1 -0
- package/dist/scene/placement.d.ts +43 -0
- package/dist/scene/placement.d.ts.map +1 -0
- package/dist/scene/placement.js +107 -0
- package/dist/scene/placement.js.map +1 -0
- package/dist/scene/raster.d.ts +18 -0
- package/dist/scene/raster.d.ts.map +1 -0
- package/dist/scene/raster.js +31 -0
- package/dist/scene/raster.js.map +1 -0
- package/dist/scene/scene-kit.d.ts +20 -0
- package/dist/scene/scene-kit.d.ts.map +1 -0
- package/dist/scene/scene-kit.js +74 -0
- package/dist/scene/scene-kit.js.map +1 -0
- package/dist/scene/scene-verbs.d.ts +4 -0
- package/dist/scene/scene-verbs.d.ts.map +1 -0
- package/dist/scene/scene-verbs.js +5 -0
- package/dist/scene/scene-verbs.js.map +1 -0
- package/dist/scene/seeded-random.d.ts +20 -0
- package/dist/scene/seeded-random.d.ts.map +1 -0
- package/dist/scene/seeded-random.js +42 -0
- package/dist/scene/seeded-random.js.map +1 -0
- package/dist/scene/spec-expression.d.ts +25 -0
- package/dist/scene/spec-expression.d.ts.map +1 -0
- package/dist/scene/spec-expression.js +234 -0
- package/dist/scene/spec-expression.js.map +1 -0
- package/dist/scene/static-server.d.ts +13 -0
- package/dist/scene/static-server.d.ts.map +1 -0
- package/dist/scene/static-server.js +99 -0
- package/dist/scene/static-server.js.map +1 -0
- package/open-design.json +2 -2
- package/package.json +3 -1
- package/scene-kit/starter/camera.js +10 -0
- package/scene-kit/starter/gobo.js +60 -0
- package/scene-kit/starter/index.html +23 -0
- package/scene-kit/starter/lib.js +84 -0
- package/scene-kit/starter/main.js +78 -0
- package/scene-kit/starter/scene.js +33 -0
- package/scene-kit/vendor/LICENSE +21 -0
- package/scene-kit/vendor/addons/geometries/RoundedBoxGeometry.js +216 -0
- package/scene-kit/vendor/addons/renderers/CSS3DRenderer.js +454 -0
- package/scene-kit/vendor/three.core.js +60586 -0
- package/scene-kit/vendor/three.module.js +19719 -0
- package/skills/12ui-design/SKILL.md +7 -1
- package/skills/12ui-design/scene.md +172 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small seeded PRNG for the scene solvers' multistart.
|
|
3
|
+
*
|
|
4
|
+
* The camera and rectangle solves draw hundreds of random starting points and
|
|
5
|
+
* keep the best local minimum. Their answer must not depend on the draw (the
|
|
6
|
+
* Python solver gave the identical camera for five different seeds), but a
|
|
7
|
+
* fixed seed keeps two runs of the same spec byte-identical, which is what
|
|
8
|
+
* makes a parity fixture meaningful. sfc32 is fast, has a 128-bit state and no
|
|
9
|
+
* short cycles for any seed.
|
|
10
|
+
*/
|
|
11
|
+
export type SeededRandom = {
|
|
12
|
+
/** Uniform in [0, 1). */
|
|
13
|
+
next: () => number;
|
|
14
|
+
/** Uniform in [low, high). */
|
|
15
|
+
uniform: (low: number, high: number) => number;
|
|
16
|
+
/** Standard normal (Box-Muller, the spare value is kept for the next call). */
|
|
17
|
+
normal: () => number;
|
|
18
|
+
};
|
|
19
|
+
export declare const seededRandom: (seed: number) => SeededRandom;
|
|
20
|
+
//# sourceMappingURL=seeded-random.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeded-random.d.ts","sourceRoot":"","sources":["../../src/scene/seeded-random.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,yBAAyB;IACzB,IAAI,EAAE,MAAM,MAAM,CAAC;IACnB,8BAA8B;IAC9B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,+EAA+E;IAC/E,MAAM,EAAE,MAAM,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,YAmC3C,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const seededRandom = (seed) => {
|
|
2
|
+
let a = 0x9e3779b9 ^ (seed >>> 0);
|
|
3
|
+
let b = 0x243f6a88 ^ Math.imul(seed >>> 0, 0x85ebca6b);
|
|
4
|
+
let c = 0xb7e15162;
|
|
5
|
+
let d = 1;
|
|
6
|
+
const next = () => {
|
|
7
|
+
a >>>= 0;
|
|
8
|
+
b >>>= 0;
|
|
9
|
+
c >>>= 0;
|
|
10
|
+
d >>>= 0;
|
|
11
|
+
const t = (a + b + d) >>> 0;
|
|
12
|
+
d = (d + 1) >>> 0;
|
|
13
|
+
a = b ^ (b >>> 9);
|
|
14
|
+
b = (c + (c << 3)) >>> 0;
|
|
15
|
+
c = (c << 21) | (c >>> 11);
|
|
16
|
+
c = (c + t) >>> 0;
|
|
17
|
+
return t / 4294967296;
|
|
18
|
+
};
|
|
19
|
+
// Discard the warm-up outputs so nearby seeds decorrelate.
|
|
20
|
+
for (let index = 0; index < 16; index += 1)
|
|
21
|
+
next();
|
|
22
|
+
let spare = null;
|
|
23
|
+
return {
|
|
24
|
+
next,
|
|
25
|
+
uniform: (low, high) => low + (high - low) * next(),
|
|
26
|
+
normal: () => {
|
|
27
|
+
if (spare !== null) {
|
|
28
|
+
const value = spare;
|
|
29
|
+
spare = null;
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
let u = 0;
|
|
33
|
+
while (u <= Number.EPSILON)
|
|
34
|
+
u = next();
|
|
35
|
+
const v = next();
|
|
36
|
+
const radius = Math.sqrt(-2 * Math.log(u));
|
|
37
|
+
spare = radius * Math.sin(2 * Math.PI * v);
|
|
38
|
+
return radius * Math.cos(2 * Math.PI * v);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=seeded-random.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeded-random.js","sourceRoot":"","sources":["../../src/scene/seeded-random.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAgB,EAAE;IACzD,IAAI,CAAC,GAAG,UAAU,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;IACvD,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,IAAI,GAAG,GAAW,EAAE;QACxB,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAClB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3B,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAClB,OAAO,CAAC,GAAG,UAAU,CAAC;IACxB,CAAC,CAAC;IACF,2DAA2D;IAC3D,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC;QAAE,IAAI,EAAE,CAAC;IACnD,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE;QACnD,MAAM,EAAE,GAAG,EAAE;YACX,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,MAAM,KAAK,GAAG,KAAK,CAAC;gBACpB,KAAK,GAAG,IAAI,CAAC;gBACb,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO;gBAAE,CAAC,GAAG,IAAI,EAAE,CAAC;YACvC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3C,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3C,OAAO,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC5C,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A safe compiler for the maths a camera spec writes in its world coordinates.
|
|
3
|
+
*
|
|
4
|
+
* Specs written for the Python solver use Python expression syntax ("D/2",
|
|
5
|
+
* "-0.5*cos(t)+D/2*sin(t)", "W**2"), which Python evaluated with `eval`. This
|
|
6
|
+
* accepts exactly the arithmetic subset: numbers, the spec's free names, the
|
|
7
|
+
* constants pi e tau inf nan, `+ - * / // % **`, unary signs, parentheses, and
|
|
8
|
+
* calls to the math functions below. Anything else (attribute access, strings,
|
|
9
|
+
* subscripts, unknown names) is refused with the landmark and the offending
|
|
10
|
+
* token named, before any solve starts.
|
|
11
|
+
*/
|
|
12
|
+
export type CompiledExpression = (values: ArrayLike<number>) => number;
|
|
13
|
+
export declare class SpecExpressionError extends Error {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
export declare const SPEC_MATH_FUNCTIONS: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Compiles one coordinate expression. `names` are the spec's free names in the
|
|
19
|
+
* order the solver passes their values; a free name shadows a constant or
|
|
20
|
+
* function of the same spelling, as it did in the Python evaluator.
|
|
21
|
+
*/
|
|
22
|
+
export declare const compileSpecExpression: (source: string, names: readonly string[], where: string) => CompiledExpression;
|
|
23
|
+
/** A spec coordinate: a JSON number, or an expression string. */
|
|
24
|
+
export declare const compileSpecCoordinate: (value: unknown, names: readonly string[], where: string) => CompiledExpression;
|
|
25
|
+
//# sourceMappingURL=spec-expression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-expression.d.ts","sourceRoot":"","sources":["../../src/scene/spec-expression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC;AAEvE,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAqDD,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAAgC,CAAC;AA2ClF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,EACd,OAAO,SAAS,MAAM,EAAE,EACxB,OAAO,MAAM,KACZ,kBAsHF,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,qBAAqB,GAChC,OAAO,OAAO,EACd,OAAO,SAAS,MAAM,EAAE,EACxB,OAAO,MAAM,KACZ,kBAIF,CAAC"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A safe compiler for the maths a camera spec writes in its world coordinates.
|
|
3
|
+
*
|
|
4
|
+
* Specs written for the Python solver use Python expression syntax ("D/2",
|
|
5
|
+
* "-0.5*cos(t)+D/2*sin(t)", "W**2"), which Python evaluated with `eval`. This
|
|
6
|
+
* accepts exactly the arithmetic subset: numbers, the spec's free names, the
|
|
7
|
+
* constants pi e tau inf nan, `+ - * / // % **`, unary signs, parentheses, and
|
|
8
|
+
* calls to the math functions below. Anything else (attribute access, strings,
|
|
9
|
+
* subscripts, unknown names) is refused with the landmark and the offending
|
|
10
|
+
* token named, before any solve starts.
|
|
11
|
+
*/
|
|
12
|
+
export class SpecExpressionError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'SpecExpressionError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const pythonMod = (a, b) => {
|
|
19
|
+
if (b === 0)
|
|
20
|
+
return Number.NaN;
|
|
21
|
+
const r = a % b;
|
|
22
|
+
return r !== 0 && (r < 0) !== (b < 0) ? r + b : r;
|
|
23
|
+
};
|
|
24
|
+
const MATH_FUNCTIONS = {
|
|
25
|
+
sin: { arity: [1, 1], apply: Math.sin },
|
|
26
|
+
cos: { arity: [1, 1], apply: Math.cos },
|
|
27
|
+
tan: { arity: [1, 1], apply: Math.tan },
|
|
28
|
+
asin: { arity: [1, 1], apply: Math.asin },
|
|
29
|
+
acos: { arity: [1, 1], apply: Math.acos },
|
|
30
|
+
atan: { arity: [1, 1], apply: Math.atan },
|
|
31
|
+
atan2: { arity: [2, 2], apply: Math.atan2 },
|
|
32
|
+
sinh: { arity: [1, 1], apply: Math.sinh },
|
|
33
|
+
cosh: { arity: [1, 1], apply: Math.cosh },
|
|
34
|
+
tanh: { arity: [1, 1], apply: Math.tanh },
|
|
35
|
+
asinh: { arity: [1, 1], apply: Math.asinh },
|
|
36
|
+
acosh: { arity: [1, 1], apply: Math.acosh },
|
|
37
|
+
atanh: { arity: [1, 1], apply: Math.atanh },
|
|
38
|
+
exp: { arity: [1, 1], apply: Math.exp },
|
|
39
|
+
exp2: { arity: [1, 1], apply: (x) => 2 ** x },
|
|
40
|
+
expm1: { arity: [1, 1], apply: Math.expm1 },
|
|
41
|
+
log: { arity: [1, 2], apply: (x, base) => (base === undefined ? Math.log(x) : Math.log(x) / Math.log(base)) },
|
|
42
|
+
log10: { arity: [1, 1], apply: Math.log10 },
|
|
43
|
+
log2: { arity: [1, 1], apply: Math.log2 },
|
|
44
|
+
log1p: { arity: [1, 1], apply: Math.log1p },
|
|
45
|
+
sqrt: { arity: [1, 1], apply: Math.sqrt },
|
|
46
|
+
cbrt: { arity: [1, 1], apply: Math.cbrt },
|
|
47
|
+
pow: { arity: [2, 2], apply: (x, y) => x ** y },
|
|
48
|
+
fabs: { arity: [1, 1], apply: Math.abs },
|
|
49
|
+
floor: { arity: [1, 1], apply: Math.floor },
|
|
50
|
+
ceil: { arity: [1, 1], apply: Math.ceil },
|
|
51
|
+
trunc: { arity: [1, 1], apply: Math.trunc },
|
|
52
|
+
degrees: { arity: [1, 1], apply: (x) => (x * 180) / Math.PI },
|
|
53
|
+
radians: { arity: [1, 1], apply: (x) => (x * Math.PI) / 180 },
|
|
54
|
+
hypot: { arity: [1, 64], apply: Math.hypot },
|
|
55
|
+
copysign: { arity: [2, 2], apply: (x, y) => (Object.is(Math.sign(y), -0) || y < 0 ? -Math.abs(x) : Math.abs(x)) },
|
|
56
|
+
fmod: { arity: [2, 2], apply: (x, y) => x % y },
|
|
57
|
+
};
|
|
58
|
+
const CONSTANTS = {
|
|
59
|
+
pi: Math.PI,
|
|
60
|
+
e: Math.E,
|
|
61
|
+
tau: 2 * Math.PI,
|
|
62
|
+
inf: Number.POSITIVE_INFINITY,
|
|
63
|
+
nan: Number.NaN,
|
|
64
|
+
};
|
|
65
|
+
export const SPEC_MATH_FUNCTIONS = Object.keys(MATH_FUNCTIONS);
|
|
66
|
+
const tokenize = (source, where) => {
|
|
67
|
+
const tokens = [];
|
|
68
|
+
let index = 0;
|
|
69
|
+
while (index < source.length) {
|
|
70
|
+
const rest = source.slice(index);
|
|
71
|
+
const space = /^\s+/u.exec(rest);
|
|
72
|
+
if (space) {
|
|
73
|
+
index += space[0].length;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const number = /^(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/u.exec(rest);
|
|
77
|
+
if (number) {
|
|
78
|
+
tokens.push({ kind: 'number', value: Number(number[0]), text: number[0] });
|
|
79
|
+
index += number[0].length;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const name = /^[A-Za-z_][A-Za-z0-9_]*/u.exec(rest);
|
|
83
|
+
if (name) {
|
|
84
|
+
tokens.push({ kind: 'name', text: name[0] });
|
|
85
|
+
index += name[0].length;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const op = /^(?:\*\*|\/\/|[-+*/%(),])/u.exec(rest);
|
|
89
|
+
if (op) {
|
|
90
|
+
tokens.push({ kind: 'op', text: op[0] });
|
|
91
|
+
index += op[0].length;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
throw new SpecExpressionError(`${where}: "${source}" uses ${JSON.stringify(rest[0])}, which a spec expression cannot contain.`
|
|
95
|
+
+ ' Use numbers, the free names, + - * / // % **, parentheses and math functions.');
|
|
96
|
+
}
|
|
97
|
+
return tokens;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Compiles one coordinate expression. `names` are the spec's free names in the
|
|
101
|
+
* order the solver passes their values; a free name shadows a constant or
|
|
102
|
+
* function of the same spelling, as it did in the Python evaluator.
|
|
103
|
+
*/
|
|
104
|
+
export const compileSpecExpression = (source, names, where) => {
|
|
105
|
+
const tokens = tokenize(source, where);
|
|
106
|
+
let position = 0;
|
|
107
|
+
const peek = () => tokens[position];
|
|
108
|
+
const isOp = (text) => {
|
|
109
|
+
const token = peek();
|
|
110
|
+
return token?.kind === 'op' && token.text === text;
|
|
111
|
+
};
|
|
112
|
+
const fail = (detail) => {
|
|
113
|
+
throw new SpecExpressionError(`${where}: "${source}" ${detail}`);
|
|
114
|
+
};
|
|
115
|
+
const expect = (text) => {
|
|
116
|
+
if (!isOp(text))
|
|
117
|
+
fail(`expects ${JSON.stringify(text)} at ${describe(peek())}`);
|
|
118
|
+
position += 1;
|
|
119
|
+
};
|
|
120
|
+
const describe = (token) => (token ? JSON.stringify(token.text) : 'the end');
|
|
121
|
+
const expression = () => {
|
|
122
|
+
let left = term();
|
|
123
|
+
while (isOp('+') || isOp('-')) {
|
|
124
|
+
const op = peek().text;
|
|
125
|
+
position += 1;
|
|
126
|
+
const right = term();
|
|
127
|
+
const l = left;
|
|
128
|
+
left = op === '+' ? (v) => l(v) + right(v) : (v) => l(v) - right(v);
|
|
129
|
+
}
|
|
130
|
+
return left;
|
|
131
|
+
};
|
|
132
|
+
const term = () => {
|
|
133
|
+
let left = unary();
|
|
134
|
+
while (isOp('*') || isOp('/') || isOp('//') || isOp('%')) {
|
|
135
|
+
const op = peek().text;
|
|
136
|
+
position += 1;
|
|
137
|
+
const right = unary();
|
|
138
|
+
const l = left;
|
|
139
|
+
left = op === '*' ? (v) => l(v) * right(v)
|
|
140
|
+
: op === '/' ? (v) => l(v) / right(v)
|
|
141
|
+
: op === '//' ? (v) => Math.floor(l(v) / right(v))
|
|
142
|
+
: (v) => pythonMod(l(v), right(v));
|
|
143
|
+
}
|
|
144
|
+
return left;
|
|
145
|
+
};
|
|
146
|
+
const unary = () => {
|
|
147
|
+
if (isOp('-')) {
|
|
148
|
+
position += 1;
|
|
149
|
+
const operand = unary();
|
|
150
|
+
return (v) => -operand(v);
|
|
151
|
+
}
|
|
152
|
+
if (isOp('+')) {
|
|
153
|
+
position += 1;
|
|
154
|
+
return unary();
|
|
155
|
+
}
|
|
156
|
+
return power();
|
|
157
|
+
};
|
|
158
|
+
// Python: power ::= primary ["**" u_expr], so -2**2 is -4 and 2**-1 is 0.5.
|
|
159
|
+
const power = () => {
|
|
160
|
+
const base = atom();
|
|
161
|
+
if (!isOp('**'))
|
|
162
|
+
return base;
|
|
163
|
+
position += 1;
|
|
164
|
+
const exponent = unary();
|
|
165
|
+
return (v) => base(v) ** exponent(v);
|
|
166
|
+
};
|
|
167
|
+
const atom = () => {
|
|
168
|
+
const token = peek();
|
|
169
|
+
if (!token)
|
|
170
|
+
return fail('ends where a value is expected');
|
|
171
|
+
if (token.kind === 'number') {
|
|
172
|
+
position += 1;
|
|
173
|
+
const value = token.value;
|
|
174
|
+
return () => value;
|
|
175
|
+
}
|
|
176
|
+
if (isOp('(')) {
|
|
177
|
+
position += 1;
|
|
178
|
+
const inner = expression();
|
|
179
|
+
expect(')');
|
|
180
|
+
return inner;
|
|
181
|
+
}
|
|
182
|
+
if (token.kind === 'name') {
|
|
183
|
+
position += 1;
|
|
184
|
+
const freeIndex = names.indexOf(token.text);
|
|
185
|
+
if (freeIndex >= 0) {
|
|
186
|
+
if (isOp('('))
|
|
187
|
+
fail(`calls the free name ${JSON.stringify(token.text)} as a function`);
|
|
188
|
+
return (v) => v[freeIndex];
|
|
189
|
+
}
|
|
190
|
+
const fn = Object.hasOwn(MATH_FUNCTIONS, token.text) ? MATH_FUNCTIONS[token.text] : undefined;
|
|
191
|
+
if (fn) {
|
|
192
|
+
if (!isOp('('))
|
|
193
|
+
fail(`uses the function ${JSON.stringify(token.text)} without calling it`);
|
|
194
|
+
position += 1;
|
|
195
|
+
const args = [];
|
|
196
|
+
if (!isOp(')')) {
|
|
197
|
+
args.push(expression());
|
|
198
|
+
while (isOp(',')) {
|
|
199
|
+
position += 1;
|
|
200
|
+
if (isOp(')'))
|
|
201
|
+
break;
|
|
202
|
+
args.push(expression());
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
expect(')');
|
|
206
|
+
const [min, max] = fn.arity;
|
|
207
|
+
if (args.length < min || args.length > max) {
|
|
208
|
+
fail(`calls ${token.text} with ${args.length} argument(s)`);
|
|
209
|
+
}
|
|
210
|
+
return (v) => fn.apply(...args.map((arg) => arg(v)));
|
|
211
|
+
}
|
|
212
|
+
if (Object.hasOwn(CONSTANTS, token.text)) {
|
|
213
|
+
const value = CONSTANTS[token.text];
|
|
214
|
+
return () => value;
|
|
215
|
+
}
|
|
216
|
+
return fail(`names ${JSON.stringify(token.text)}, which is neither a free name`
|
|
217
|
+
+ ` (${names.length ? names.join(', ') : 'none declared'}) nor a math function or constant.`);
|
|
218
|
+
}
|
|
219
|
+
return fail(`has ${describe(token)} where a value is expected`);
|
|
220
|
+
};
|
|
221
|
+
const compiled = expression();
|
|
222
|
+
if (position < tokens.length)
|
|
223
|
+
fail(`has an unexpected ${describe(peek())}`);
|
|
224
|
+
return compiled;
|
|
225
|
+
};
|
|
226
|
+
/** A spec coordinate: a JSON number, or an expression string. */
|
|
227
|
+
export const compileSpecCoordinate = (value, names, where) => {
|
|
228
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
229
|
+
return () => value;
|
|
230
|
+
if (typeof value === 'string')
|
|
231
|
+
return compileSpecExpression(value, names, where);
|
|
232
|
+
throw new SpecExpressionError(`${where}: ${JSON.stringify(value)} is not a number or an expression string`);
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=spec-expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-expression.js","sourceRoot":"","sources":["../../src/scene/spec-expression.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const contentTypeFor: (file: string) => string;
|
|
2
|
+
/** The file a request path names inside `root`, or null when it would leave the folder. */
|
|
3
|
+
export declare const resolveInsideRoot: (root: string, requestPath: string) => string | null;
|
|
4
|
+
export type StaticServer = {
|
|
5
|
+
url: string;
|
|
6
|
+
port: number;
|
|
7
|
+
close: () => Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export declare const startStaticServer: (args: {
|
|
10
|
+
directory: string;
|
|
11
|
+
port: number;
|
|
12
|
+
}) => Promise<StaticServer>;
|
|
13
|
+
//# sourceMappingURL=static-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-server.d.ts","sourceRoot":"","sources":["../../src/scene/static-server.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,MAE7C,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,aAAa,MAAM,KAAG,MAAM,GAAG,IAW9E,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAErF,eAAO,MAAM,iBAAiB,GAAU,MAAM;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KAAG,OAAO,CAAC,YAAY,CA8CvG,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
2
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
3
|
+
import http from 'node:http';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
/**
|
|
6
|
+
* A static file server for one scene folder, bound to 127.0.0.1 only. It
|
|
7
|
+
* serves nothing outside the folder (a `..` or a symlink out of it is a 403)
|
|
8
|
+
* and never caches, so every capture sees the files as they are now.
|
|
9
|
+
*/
|
|
10
|
+
const CONTENT_TYPES = {
|
|
11
|
+
'.html': 'text/html; charset=utf-8',
|
|
12
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
13
|
+
'.mjs': 'text/javascript; charset=utf-8',
|
|
14
|
+
'.css': 'text/css; charset=utf-8',
|
|
15
|
+
'.png': 'image/png',
|
|
16
|
+
'.jpg': 'image/jpeg',
|
|
17
|
+
'.jpeg': 'image/jpeg',
|
|
18
|
+
'.webp': 'image/webp',
|
|
19
|
+
'.svg': 'image/svg+xml',
|
|
20
|
+
'.json': 'application/json',
|
|
21
|
+
'.glb': 'model/gltf-binary',
|
|
22
|
+
'.gltf': 'model/gltf+json',
|
|
23
|
+
'.bin': 'application/octet-stream',
|
|
24
|
+
'.ttf': 'font/ttf',
|
|
25
|
+
'.otf': 'font/otf',
|
|
26
|
+
'.woff': 'font/woff',
|
|
27
|
+
'.woff2': 'font/woff2',
|
|
28
|
+
};
|
|
29
|
+
export const contentTypeFor = (file) => (CONTENT_TYPES[path.extname(file).toLowerCase()] ?? 'application/octet-stream');
|
|
30
|
+
/** The file a request path names inside `root`, or null when it would leave the folder. */
|
|
31
|
+
export const resolveInsideRoot = (root, requestPath) => {
|
|
32
|
+
let decoded;
|
|
33
|
+
try {
|
|
34
|
+
decoded = decodeURIComponent(requestPath.split('?')[0].split('#')[0]);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (decoded.includes('\0'))
|
|
40
|
+
return null;
|
|
41
|
+
const resolved = path.resolve(root, `.${path.posix.normalize(`/${decoded}`)}`);
|
|
42
|
+
const withIndex = decoded.endsWith('/') ? path.join(resolved, 'index.html') : resolved;
|
|
43
|
+
return withIndex === root || withIndex.startsWith(`${root}${path.sep}`) ? withIndex : null;
|
|
44
|
+
};
|
|
45
|
+
export const startStaticServer = async (args) => {
|
|
46
|
+
const root = await realpath(path.resolve(args.directory));
|
|
47
|
+
const server = http.createServer((request, response) => {
|
|
48
|
+
const refuse = (status) => {
|
|
49
|
+
response.writeHead(status, { 'cache-control': 'no-store' });
|
|
50
|
+
response.end();
|
|
51
|
+
};
|
|
52
|
+
if (request.method !== 'GET' && request.method !== 'HEAD')
|
|
53
|
+
return refuse(405);
|
|
54
|
+
const candidate = resolveInsideRoot(root, request.url ?? '/');
|
|
55
|
+
if (!candidate)
|
|
56
|
+
return refuse(403);
|
|
57
|
+
void (async () => {
|
|
58
|
+
let file = candidate;
|
|
59
|
+
try {
|
|
60
|
+
if ((await stat(file)).isDirectory())
|
|
61
|
+
file = path.join(file, 'index.html');
|
|
62
|
+
const real = await realpath(file);
|
|
63
|
+
if (real !== root && !real.startsWith(`${root}${path.sep}`))
|
|
64
|
+
return refuse(403);
|
|
65
|
+
const info = await stat(real);
|
|
66
|
+
if (!info.isFile())
|
|
67
|
+
return refuse(404);
|
|
68
|
+
response.writeHead(200, {
|
|
69
|
+
'content-type': contentTypeFor(real),
|
|
70
|
+
'content-length': info.size,
|
|
71
|
+
'cache-control': 'no-store',
|
|
72
|
+
});
|
|
73
|
+
if (request.method === 'HEAD')
|
|
74
|
+
return response.end();
|
|
75
|
+
createReadStream(real).on('error', () => response.destroy()).pipe(response);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
refuse(404);
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
});
|
|
82
|
+
await new Promise((resolve, reject) => {
|
|
83
|
+
server.once('error', reject);
|
|
84
|
+
server.listen(args.port, '127.0.0.1', () => {
|
|
85
|
+
server.off('error', reject);
|
|
86
|
+
resolve();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
const port = server.address().port;
|
|
90
|
+
return {
|
|
91
|
+
url: `http://127.0.0.1:${port}/`,
|
|
92
|
+
port,
|
|
93
|
+
close: () => new Promise((resolve) => {
|
|
94
|
+
server.close(() => resolve());
|
|
95
|
+
server.closeAllConnections();
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=static-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-server.js","sourceRoot":"","sources":["../../src/scene/static-server.ts"],"names":[],"mappings":"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"}
|
package/open-design.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"specVersion": "1.0.0",
|
|
4
4
|
"name": "12ui-design",
|
|
5
5
|
"title": "12UI Design",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.72",
|
|
7
7
|
"description": "Explore corpus-grounded interface directions, choose one, and convert it into production-ready responsive or native artifacts with 12UI.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Just Every",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"command": "npx",
|
|
50
50
|
"args": [
|
|
51
51
|
"-y",
|
|
52
|
-
"@12ui/design@0.2.
|
|
52
|
+
"@12ui/design@0.2.72",
|
|
53
53
|
"mcp"
|
|
54
54
|
]
|
|
55
55
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12ui/design",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.72",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The canonical 12ui CLI, SDK, MCP server, and visual interface design skills",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"dist/**/*",
|
|
13
13
|
"prototype-kit/**/*",
|
|
14
|
+
"scene-kit/**/*",
|
|
14
15
|
"skills/**/*",
|
|
15
16
|
"runtime/**/*",
|
|
16
17
|
"SKILL.md",
|
|
@@ -25,6 +26,7 @@
|
|
|
25
26
|
"test": "node ../../scripts/codex-runtime-bundle.mjs && node ../../scripts/sync-improve-core.mjs && vitest",
|
|
26
27
|
"test:run": "node ../../scripts/codex-runtime-bundle.mjs && node ../../scripts/sync-improve-core.mjs && tsc --noEmit && vitest run",
|
|
27
28
|
"test:prototype": "node prototype-kit/polish/tests/rules-check.mjs && node prototype-kit/polish/tests/harm-check.mjs && node prototype-kit/polish/tests/breathing-check.mjs && node prototype-kit/polish/tests/report-check.mjs && node prototype-kit/tests/rules-check.mjs && node prototype-kit/tests/offline-check.mjs && node prototype-kit/tests/shell-stitching-runtime.mjs && node prototype-kit/tests/shell-stitching-runtime.mjs --reintroduce-defect && node prototype-kit/tests/adversarial-runtime.mjs --assert",
|
|
29
|
+
"test:scene": "npm run build && node scripts/scene-starter-check.mjs",
|
|
28
30
|
"typecheck": "node ../../scripts/sync-improve-core.mjs && tsc --noEmit && tsc -p tsconfig.type-tests.json",
|
|
29
31
|
"prepublishOnly": "npm run build"
|
|
30
32
|
},
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Paste the "camera.js" line from `12ui scene solve spec.json --out cam.json` here. It uses the same keys and units as cam.json:
|
|
2
|
+
// yawDeg / pitchDeg / rollDeg / vfov are DEGREES (main.js converts to radians). Scene units are yours.
|
|
3
|
+
export const FRAME = { w: 1536, h: 1024 }; // source image size = design frame
|
|
4
|
+
export const CAM = { pos: [0, 2, 6], yawDeg: 0, pitchDeg: -11, rollDeg: 0, vfov: 30 };
|
|
5
|
+
// Depth range: tight. Keep far/near under ~100 or headless GL z-fights (edge-on cylinder walls, decals sitting 1 mm off a
|
|
6
|
+
// surface, shadow-map leaks that show as dotted arcs). The solver prints a "clip" for this camera; also keep coincident
|
|
7
|
+
// surfaces at least ~0.002 x the camera distance apart, and give thin parts real thickness or a chamfered rim.
|
|
8
|
+
export const CLIP = { near: 0.5, far: 60 };
|
|
9
|
+
export const PIVOT = [0, 0, 0]; // orbit centre: where the optical axis meets the subject
|
|
10
|
+
export const ORBIT_LIMIT = 15; // degrees each way for pointer/drag orbit
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Window light, leaf shadows, dappled patterns: a SpotLight with `.map` (a gobo). The pattern is DRAWN IN SOURCE-IMAGE
|
|
2
|
+
// PIXELS (where the shadows sit in the design) and resampled through the solved camera onto the surface plane, so it lands
|
|
3
|
+
// exactly where drawn at the landing view but is real light: it darkens whatever stands there and moves with the orbit.
|
|
4
|
+
//
|
|
5
|
+
// const spot = new THREE.SpotLight(0xfff1e0, 900, 0, 0.6, 0.7, 2); // angle = half-angle: it must cover the whole visible surface
|
|
6
|
+
// spot.position.set(-2, 8, 3); spot.target.position.set(0.5, 0, 0); spot.castShadow = true; // .map is ignored unless castShadow is true
|
|
7
|
+
// scene.add(spot, spot.target);
|
|
8
|
+
// spot.map = goboTexture(spot, (g, w, h) => { g.fillStyle = '#fff'; g.fillRect(0, 0, w, h); // white = light, dark = shadow
|
|
9
|
+
// g.filter = 'blur(38px)'; g.fillStyle = 'rgba(120,98,86,0.5)'; /* draw shadow shapes at source pixel coordinates */ });
|
|
10
|
+
//
|
|
11
|
+
// Orientation, pinned down: texture rows run from the spot's view-bottom (row 0) to view-top, flipY = false, and the pattern
|
|
12
|
+
// is written through CAM (camera.js), not through the light. So DO NOT flip or mirror anything by hand. Verify once by
|
|
13
|
+
// drawing a bright red square at a known source pixel (say the top-left of the sleeve) and checking that it lands there.
|
|
14
|
+
// Draw past the frame edges: outside the source frame the edge value is held, so patterns fade instead of ending in a seam.
|
|
15
|
+
import * as THREE from 'three';
|
|
16
|
+
import { CAM, FRAME } from './camera.js';
|
|
17
|
+
import { rng } from './lib.js';
|
|
18
|
+
|
|
19
|
+
const D2R = THREE.MathUtils.degToRad;
|
|
20
|
+
|
|
21
|
+
export function goboTexture(light, draw, { size = 1536, planeY = 0 } = {}) {
|
|
22
|
+
const src = document.createElement('canvas'); src.width = FRAME.w; src.height = FRAME.h;
|
|
23
|
+
draw(src.getContext('2d'), FRAME.w, FRAME.h);
|
|
24
|
+
const sd = src.getContext('2d').getImageData(0, 0, FRAME.w, FRAME.h).data;
|
|
25
|
+
const P = light.position.clone(), T = light.target.position.clone();
|
|
26
|
+
const f = T.clone().sub(P).normalize();
|
|
27
|
+
const right = new THREE.Vector3().crossVectors(f, new THREE.Vector3(0, 1, 0)).normalize(), up = new THREE.Vector3().crossVectors(right, f);
|
|
28
|
+
const th = Math.tan(light.angle);
|
|
29
|
+
const q = new THREE.Quaternion().setFromEuler(new THREE.Euler(D2R(CAM.pitchDeg), D2R(CAM.yawDeg), D2R(CAM.rollDeg), 'YXZ')).invert();
|
|
30
|
+
const F = (FRAME.h / 2) / Math.tan(D2R(CAM.vfov) / 2), cp = new THREE.Vector3(...CAM.pos), d = new THREE.Vector3(), c = new THREE.Vector3();
|
|
31
|
+
const out = document.createElement('canvas'); out.width = out.height = size;
|
|
32
|
+
const og = out.getContext('2d'), od = og.createImageData(size, size);
|
|
33
|
+
for (let j = 0; j < size; j++) for (let i = 0; i < size; i++) {
|
|
34
|
+
const x = 2 * (i + 0.5) / size - 1, y = 2 * (j + 0.5) / size - 1;
|
|
35
|
+
d.copy(f).addScaledVector(right, x * th).addScaledVector(up, y * th);
|
|
36
|
+
let v = 255;
|
|
37
|
+
if (d.y < -1e-4) { // ray from the light hits the plane y = planeY
|
|
38
|
+
const t = (planeY - P.y) / d.y;
|
|
39
|
+
c.set(P.x + d.x * t, planeY, P.z + d.z * t).sub(cp).applyQuaternion(q); // world -> camera
|
|
40
|
+
const u = FRAME.w / 2 + F * c.x / -c.z, w = FRAME.h / 2 - F * c.y / -c.z;
|
|
41
|
+
const ux = Math.min(FRAME.w - 1, Math.max(0, Math.round(u))), uy = Math.min(FRAME.h - 1, Math.max(0, Math.round(w)));
|
|
42
|
+
v = sd[(uy * FRAME.w + ux) * 4];
|
|
43
|
+
}
|
|
44
|
+
const o = (j * size + i) * 4; od.data[o] = od.data[o + 1] = od.data[o + 2] = v; od.data[o + 3] = 255;
|
|
45
|
+
}
|
|
46
|
+
og.putImageData(od, 0, 0);
|
|
47
|
+
const t = new THREE.CanvasTexture(out); t.flipY = false; t.colorSpace = THREE.SRGBColorSpace; return t;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// One leaf/frond as ONE path segment set. Call several times between g.beginPath() and a single g.fill() so overlapping
|
|
51
|
+
// leaflets union into one shadow (separate fills stack and read as dark blotches).
|
|
52
|
+
export function frond(g, x, y, ang, len, wid, seed = 4) {
|
|
53
|
+
const r = rng(seed + Math.round(x + y)); g.save(); g.translate(x, y); g.rotate(ang);
|
|
54
|
+
g.moveTo(0, 0); g.quadraticCurveTo(len * 0.5, -len * 0.03, len, 0); g.quadraticCurveTo(len * 0.5, len * 0.03, 0, 0);
|
|
55
|
+
for (let i = 0; i < 6; i++) {
|
|
56
|
+
const t = 0.2 + i * 0.13, px = len * t, l = len * 0.36 * Math.sin(Math.PI * (0.1 + t * 0.8));
|
|
57
|
+
for (const s of [-1, 1]) { g.save(); g.translate(px, 0); g.rotate(s * (0.95 + r() * 0.2)); g.moveTo(l, 0); g.ellipse(l / 2, 0, l / 2, wid * (0.8 + r() * 0.4), 0, 0, 7); g.restore(); }
|
|
58
|
+
}
|
|
59
|
+
g.restore();
|
|
60
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Scene</title>
|
|
7
|
+
<style>
|
|
8
|
+
html, body { margin: 0; height: 100%; overflow: hidden; background: #000; }
|
|
9
|
+
canvas#gl { position: fixed; inset: 0; width: 100%; height: 100%; display: block; }
|
|
10
|
+
/* #stage is the source image's pixel frame; main.js scales it to sit exactly over the contain-fitted canvas */
|
|
11
|
+
#stage { position: fixed; left: 0; top: 0; width: 1536px; height: 1024px; transform-origin: 0 0; pointer-events: none; }
|
|
12
|
+
#stage a, #stage button, #stage [data-hit] { pointer-events: auto; }
|
|
13
|
+
/* real page text goes here, positioned in source pixels, e.g.
|
|
14
|
+
<h1 style="position:absolute; left:56px; top:232px; font: 800 78px/0.9 'Chivo', sans-serif">TITLE</h1> */
|
|
15
|
+
</style>
|
|
16
|
+
<script type="importmap">{ "imports": { "three": "./vendor/three.module.js", "three/addons/": "./vendor/addons/" } }</script>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<canvas id="gl"></canvas>
|
|
20
|
+
<div id="stage"></div>
|
|
21
|
+
<script type="module" src="./main.js"></script>
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|