@heyramzi/ui 2.11.1 → 2.11.2
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/dist/agents/helpers.js +38 -38
- package/dist/agents/page-state.js +49 -50
- package/dist/agents/vendor/color.js +146 -127
- package/dist/agents/vendor/draw.js +617 -508
- package/dist/agents/vendor/engine.js +698 -599
- package/dist/agents/vendor/mesh.js +653 -683
- package/dist/agents/vendor/plastic.js +3913 -3112
- package/dist/agents/vendor/presets.js +163 -164
- package/dist/agents/vendor/shapes.js +21 -20
- package/dist/agents/vendor/ticker.js +41 -42
- package/dist/agents/vendor/types.js +1 -0
- package/dist/agents/vendor/wear.js +1421 -1448
- package/dist/ai/confirmation/confirmation-context.svelte.js +6 -6
- package/dist/ai/confirmation/index.js +3 -1
- package/dist/ai/index.js +33 -0
- package/dist/ai/prompt-input/index.js +3 -1
- package/dist/ai/reasoning/index.js +3 -1
- package/dist/ai/reasoning/reasoning-context.svelte.js +6 -6
- package/dist/ai/suggestion/index.js +3 -1
- package/dist/ai/typing-dots/index.js +3 -1
- package/dist/animations/text-swap.js +24 -24
- package/dist/brand/index.js +54 -48
- package/dist/canvas/card/resize.js +5 -3
- package/dist/canvas/edges/edge-path.js +255 -242
- package/dist/canvas/editor/context-menu.js +39 -36
- package/dist/canvas/editor/dispatch.svelte.js +154 -151
- package/dist/canvas/editor/editor-context.js +7 -2
- package/dist/canvas/editor/editor-mode.js +1 -0
- package/dist/canvas/editor/frame.js +73 -76
- package/dist/canvas/editor/hand-context.js +5 -2
- package/dist/canvas/editor/history-core.js +163 -191
- package/dist/canvas/editor/keymap.js +95 -169
- package/dist/canvas/editor/layout-actions.js +143 -144
- package/dist/canvas/editor/lock.js +52 -51
- package/dist/canvas/editor/rebase.js +217 -240
- package/dist/canvas/editor/style.js +132 -137
- package/dist/canvas/editor/text-field-context.js +7 -2
- package/dist/canvas/editor/z-order.js +65 -70
- package/dist/canvas/headless/alignment.js +22 -16
- package/dist/canvas/headless/break-apart-find.js +286 -279
- package/dist/canvas/headless/break-apart-pieces.js +62 -58
- package/dist/canvas/headless/break-apart-plan.js +299 -325
- package/dist/canvas/headless/break-apart-style.js +75 -70
- package/dist/canvas/headless/break-apart.js +188 -181
- package/dist/canvas/headless/canvas-cleanup.js +78 -58
- package/dist/canvas/headless/edge-anchors.js +115 -116
- package/dist/canvas/headless/edge-memo.js +24 -22
- package/dist/canvas/headless/edge-serialization.js +17 -27
- package/dist/canvas/headless/figure-geometry.js +232 -264
- package/dist/canvas/headless/fit-viewport.js +84 -67
- package/dist/canvas/headless/grid.js +88 -86
- package/dist/canvas/headless/icon-node.js +44 -36
- package/dist/canvas/headless/index.js +1 -0
- package/dist/canvas/headless/linkify.js +17 -19
- package/dist/canvas/headless/pen-marks.js +101 -95
- package/dist/canvas/headless/sketch.js +231 -209
- package/dist/canvas/headless/snap-guides.js +99 -83
- package/dist/canvas/headless/svg-figure.js +133 -117
- package/dist/canvas/headless/table-layout.js +146 -107
- package/dist/canvas/index.js +2 -0
- package/dist/canvas/lod/lod-mode.svelte.js +20 -18
- package/dist/canvas/lod/render-all.svelte.js +36 -39
- package/dist/canvas/ops/arrange.js +78 -71
- package/dist/canvas/ops/ops.js +260 -255
- package/dist/canvas/pen/colors.js +37 -35
- package/dist/canvas/pen/eraser.js +28 -27
- package/dist/canvas/pen/geometry.js +120 -130
- package/dist/canvas/pen/index.js +1 -0
- package/dist/canvas/pen/ops.js +11 -11
- package/dist/canvas/pen/pen-session.js +150 -159
- package/dist/canvas/pen/plugin.js +107 -104
- package/dist/canvas/pen/shapes.js +73 -68
- package/dist/canvas/pen/stroke-path.js +32 -30
- package/dist/canvas/pen/types.js +21 -21
- package/dist/canvas/pen/ui.js +10 -8
- package/dist/canvas/plugin.js +46 -46
- package/dist/canvas/screen/glyphs.js +12 -11
- package/dist/canvas/screen/labels.js +22 -22
- package/dist/canvas/screen/parts.js +98 -111
- package/dist/canvas/screen/plugin.js +47 -56
- package/dist/canvas/screen/types.js +30 -35
- package/dist/canvas/screen/ui.js +7 -7
- package/dist/canvas/test-utils.js +14 -14
- package/dist/canvas/types.js +1 -0
- package/dist/canvas/ui/forward-wheel.js +24 -25
- package/dist/canvas/ui/icons.js +64 -131
- package/dist/canvas/ui/place-panel.js +58 -49
- package/dist/canvas/ui/shortcut-tip.js +14 -12
- package/dist/canvas/ui/tools.js +300 -385
- package/dist/charts/dashboard/chart.js +4 -0
- package/dist/charts/dashboard/data-table.js +11 -0
- package/dist/charts/dashboard/windows.js +8 -3
- package/dist/charts/edge-fade.js +10 -5
- package/dist/charts/hover-band.js +16 -11
- package/dist/charts/index.js +0 -1
- package/dist/charts/motion.js +11 -7
- package/dist/charts/primitives/chart-utils.js +12 -12
- package/dist/components/copy/clipboard.js +32 -32
- package/dist/components/copy/copied.svelte.js +31 -23
- package/dist/components/data-table/data-table.js +39 -39
- package/dist/components/docs/index.js +4 -0
- package/dist/components/docs/scroll-spy.svelte.js +73 -68
- package/dist/components/list-page/filter-section.js +4 -1
- package/dist/components/list-page/index.js +2 -0
- package/dist/components/list-page/list-table.js +7 -2
- package/dist/components/list-page/queue-rail.js +14 -0
- package/dist/components/list-page/section-host.js +4 -2
- package/dist/components/list-page/step.js +4 -3
- package/dist/components/list-page/types.js +1 -0
- package/dist/components/picker/trigger.js +48 -30
- package/dist/components/picker/types.js +1 -0
- package/dist/components/segmented/collapse.js +38 -27
- package/dist/components/segmented/index.js +8 -0
- package/dist/components/segmented/segmented.js +67 -42
- package/dist/components/star-rating-input/index.js +3 -1
- package/dist/components/table-view/types.js +1 -0
- package/dist/components/tags-input/tags.js +81 -69
- package/dist/components/theme/index.js +4 -0
- package/dist/components/toolbar/index.js +4 -0
- package/dist/components/toolbar/toolbar-trigger.js +14 -1
- package/dist/editor/anchors.js +17 -13
- package/dist/editor/block-id.js +52 -51
- package/dist/editor/block-ops.js +108 -80
- package/dist/editor/doc-blocks.js +155 -166
- package/dist/editor/extensions.js +139 -180
- package/dist/editor/index.js +4 -0
- package/dist/editor/markdown-test-helpers.js +4 -2
- package/dist/editor/markdown.js +420 -408
- package/dist/editor/serializer.js +204 -200
- package/dist/editor/slash-items.js +89 -101
- package/dist/editor/youtube-url.js +25 -20
- package/dist/effects/animated-beam/path-calculator.svelte.js +43 -38
- package/dist/effects/animated-beam/types.js +1 -0
- package/dist/effects/border-beam/css.js +92 -78
- package/dist/effects/border-beam/presets.js +1705 -1720
- package/dist/effects/border-beam/register-properties.js +43 -33
- package/dist/effects/border-beam/types.js +1 -0
- package/dist/effects/image-comparison/compare.js +3 -2
- package/dist/effects/image-reveal/types.js +1 -0
- package/dist/effects/magnetic/magnetic.js +58 -52
- package/dist/effects/reveal/observe.js +50 -40
- package/dist/effects/reveal/types.js +1 -0
- package/dist/effects/shader-gradient/color.js +36 -29
- package/dist/effects/shader-gradient/renderer.js +384 -295
- package/dist/effects/shader-gradient/shaders.js +31 -0
- package/dist/effects/shader-gradient/types.js +1 -0
- package/dist/icons/apple-symbol-exports.js +99 -124
- package/dist/icons/sf-symbols.js +357 -461
- package/dist/icons/trades.js +43 -28
- package/dist/inbox/index.js +1 -0
- package/dist/inbox/logic.js +126 -122
- package/dist/inbox/types.js +43 -43
- package/dist/lib/arrive.js +19 -12
- package/dist/lib/icon-types.js +1 -0
- package/dist/lib/initials.js +16 -10
- package/dist/lib/layout.js +5 -0
- package/dist/lib/panel.js +13 -0
- package/dist/lib/result.js +22 -22
- package/dist/lib/sliding-pill.js +59 -44
- package/dist/lib/theme-transition.js +43 -43
- package/dist/lib/tone.js +31 -24
- package/dist/lib/utils.js +3 -1
- package/dist/onboarding/define-tours.js +24 -23
- package/dist/onboarding/onboarding.svelte.js +16 -12
- package/dist/onboarding/pick.js +10 -6
- package/dist/onboarding/stores.js +40 -41
- package/dist/onboarding/wait.js +24 -23
- package/dist/panel/side-panel-types.js +1 -0
- package/dist/record/demo-data.js +459 -486
- package/dist/record/fields.js +129 -131
- package/dist/record/format.js +70 -82
- package/dist/record/gutter.js +1 -0
- package/dist/record/kinds.js +95 -105
- package/dist/record/logic.js +238 -219
- package/dist/record/types.js +49 -48
- package/dist/shadcn/accordion/index.js +3 -1
- package/dist/shadcn/alert/index.js +3 -1
- package/dist/shadcn/alert-dialog/index.js +3 -1
- package/dist/shadcn/attachment/index.js +3 -1
- package/dist/shadcn/avatar/index.js +3 -1
- package/dist/shadcn/breadcrumb/index.js +3 -1
- package/dist/shadcn/bubble/index.js +3 -1
- package/dist/shadcn/button/index.js +3 -1
- package/dist/shadcn/calendar/index.js +3 -1
- package/dist/shadcn/card/index.js +3 -1
- package/dist/shadcn/checkbox/index.js +3 -1
- package/dist/shadcn/code-block/index.js +3 -1
- package/dist/shadcn/collapsible/index.js +3 -1
- package/dist/shadcn/command/index.js +4 -1
- package/dist/shadcn/context-menu/index.js +3 -1
- package/dist/shadcn/dialog/index.js +3 -1
- package/dist/shadcn/dropdown-menu/menu-item.js +40 -20
- package/dist/shadcn/empty/index.js +3 -1
- package/dist/shadcn/field/index.js +3 -1
- package/dist/shadcn/hooks/highlighter.js +77 -75
- package/dist/shadcn/hooks/is-mobile.svelte.js +3 -3
- package/dist/shadcn/hooks/use-highlighted-code.svelte.js +62 -62
- package/dist/shadcn/input/index.js +3 -1
- package/dist/shadcn/kbd/index.js +3 -1
- package/dist/shadcn/label/index.js +3 -1
- package/dist/shadcn/marker/index.js +3 -1
- package/dist/shadcn/message/index.js +3 -1
- package/dist/shadcn/message-scroller/index.js +3 -1
- package/dist/shadcn/popover/index.js +3 -1
- package/dist/shadcn/progress/index.js +3 -1
- package/dist/shadcn/progress/tone.js +8 -7
- package/dist/shadcn/radio-group/index.js +3 -1
- package/dist/shadcn/scroll-area/index.js +3 -1
- package/dist/shadcn/select/index.js +3 -1
- package/dist/shadcn/separator/index.js +3 -1
- package/dist/shadcn/sheet/index.js +3 -1
- package/dist/shadcn/sidebar/context.svelte.js +42 -28
- package/dist/shadcn/sidebar/index.js +3 -1
- package/dist/shadcn/skeleton/index.js +3 -1
- package/dist/shadcn/slider/index.js +3 -1
- package/dist/shadcn/switch/index.js +3 -1
- package/dist/shadcn/table/index.js +3 -1
- package/dist/shadcn/tabs/index.js +3 -1
- package/dist/shadcn/textarea/index.js +3 -1
- package/dist/shadcn/toggle/index.js +3 -1
- package/dist/shadcn/toggle-group/index.js +3 -1
- package/dist/shadcn/tooltip/context.js +7 -2
- package/dist/shadcn/tooltip/index.js +3 -1
- package/dist/shadcn/trace/index.js +3 -1
- package/dist/slides/bridges.js +65 -59
- package/dist/slides/deck-controller.svelte.js +210 -207
- package/dist/slides/export/capture.js +66 -71
- package/dist/slides/export/ffmpeg.js +76 -72
- package/dist/slides/export/index.js +3 -0
- package/dist/slides/export/launch.js +9 -6
- package/dist/slides/export/pdf.js +60 -54
- package/dist/slides/export/types.js +1 -0
- package/dist/slides/labels.js +24 -24
- package/dist/slides/layout.js +39 -41
- package/dist/slides/model.js +9 -7
- package/dist/slides/navigation.js +78 -73
- package/dist/slides/types.js +2 -2
- package/dist/status/define-statuses.js +18 -14
- package/dist/status/status-types.js +1 -0
- package/dist/test-setup.js +24 -20
- package/dist/thread/logic.js +73 -77
- package/dist/thread/types.js +21 -21
- package/dist/work/board-order.js +94 -90
- package/dist/work/dates.js +16 -9
- package/dist/work/detail/detail-types.js +18 -18
- package/dist/work/docs/edited.js +32 -29
- package/dist/work/docs/index.js +4 -0
- package/dist/work/docs/labels.js +34 -34
- package/dist/work/docs/menu.js +113 -106
- package/dist/work/docs/outline.js +39 -29
- package/dist/work/gantt/logic.js +211 -235
- package/dist/work/inbox/inbox-logic.js +71 -75
- package/dist/work/inbox/inbox-statuses.js +75 -72
- package/dist/work/inbox/inbox-types.js +1 -0
- package/dist/work/kanban-drag.js +27 -22
- package/dist/work/kanban-types.js +1 -0
- package/dist/work/presets/sales.js +36 -32
- package/dist/work/reorder.js +9 -8
- package/dist/work/task-types.js +1 -0
- package/dist/work/time/duration.js +51 -46
- package/dist/work/time/time-types.js +23 -23
- package/dist/work/use-kanban-dnd.svelte.js +126 -125
- package/package.json +3 -1
- package/src/components/list-page/master-detail.svelte +3 -1
- package/src/components/list-page/rail-row.svelte +4 -4
- package/src/editor/markdown.ts +30 -35
|
@@ -1,727 +1,697 @@
|
|
|
1
1
|
export function makeView(m, o, L, H, px) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
|
|
20
|
-
])
|
|
21
|
-
};
|
|
2
|
+
return {
|
|
3
|
+
m,
|
|
4
|
+
o,
|
|
5
|
+
L,
|
|
6
|
+
H,
|
|
7
|
+
px,
|
|
8
|
+
toScreen: (p) => {
|
|
9
|
+
const x = p[0] - o[0], y = p[1] - o[1], z = p[2] - o[2];
|
|
10
|
+
return [m[0] * x + m[1] * y + m[2] * z, m[3] * x + m[4] * y + m[5] * z];
|
|
11
|
+
},
|
|
12
|
+
depth: (p) => m[6] * (p[0] - o[0]) + m[7] * (p[1] - o[1]) + m[8] * (p[2] - o[2]),
|
|
13
|
+
dir: (v) => norm([
|
|
14
|
+
m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
|
|
15
|
+
m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
|
|
16
|
+
m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
|
|
17
|
+
])
|
|
18
|
+
};
|
|
22
19
|
}
|
|
23
|
-
export const sub = (a, b)=>[
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export const cross = (a, b)=>[
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export const dot = (a, b)=>a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
34
|
-
export const norm = (v)=>{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
20
|
+
export const sub = (a, b) => [
|
|
21
|
+
a[0] - b[0],
|
|
22
|
+
a[1] - b[1],
|
|
23
|
+
a[2] - b[2]
|
|
24
|
+
];
|
|
25
|
+
export const cross = (a, b) => [
|
|
26
|
+
a[1] * b[2] - a[2] * b[1],
|
|
27
|
+
a[2] * b[0] - a[0] * b[2],
|
|
28
|
+
a[0] * b[1] - a[1] * b[0]
|
|
29
|
+
];
|
|
30
|
+
export const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
31
|
+
export const norm = (v) => {
|
|
32
|
+
const l = Math.hypot(v[0], v[1], v[2]) || 1;
|
|
33
|
+
return [
|
|
34
|
+
v[0] / l,
|
|
35
|
+
v[1] / l,
|
|
36
|
+
v[2] / l
|
|
37
|
+
];
|
|
41
38
|
};
|
|
42
39
|
const TAU = Math.PI * 2;
|
|
40
|
+
/** How many segments to cut a length into for this view: about one per
|
|
41
|
+
`quad` device pixels, within `lo … hi`. */
|
|
43
42
|
export function segs(view, len, lo, hi, quad = 11) {
|
|
44
|
-
|
|
43
|
+
return Math.max(lo, Math.min(hi, Math.round(len * view.px / quad)));
|
|
45
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Sample `surf(u, v)` (u, v ∈ [0, 1]) on an `nu × nv` grid. `inside` is a
|
|
47
|
+
* point inside the object, or for a tube the point on its centre line:
|
|
48
|
+
* each normal is turned to face away from it, so a surface needs no care
|
|
49
|
+
* over its winding. With `smooth`, every corner gets the mean of its
|
|
50
|
+
* cells' normals — a surface closed round u, or pinched to a pole, is
|
|
51
|
+
* joined up — for the gradient fill; without, the cells stay faceted.
|
|
52
|
+
*/
|
|
46
53
|
export function makeMesh(surf, nu, nv, inside, smooth = true) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
nv,
|
|
150
|
-
closed,
|
|
151
|
-
pos,
|
|
152
|
-
fn,
|
|
153
|
-
vn
|
|
154
|
-
};
|
|
54
|
+
const W = nu + 1, N = W * (nv + 1);
|
|
55
|
+
const pos = new Float64Array(N * 3);
|
|
56
|
+
let ext = 0;
|
|
57
|
+
for (let j = 0, k = 0; j <= nv; j++) {
|
|
58
|
+
for (let i = 0; i <= nu; i++, k++) {
|
|
59
|
+
const p = surf(i / nu, j / nv);
|
|
60
|
+
pos[k * 3] = p[0];
|
|
61
|
+
pos[k * 3 + 1] = p[1];
|
|
62
|
+
pos[k * 3 + 2] = p[2];
|
|
63
|
+
ext = Math.max(ext, Math.abs(p[0] - pos[0]) + Math.abs(p[1] - pos[1]) + Math.abs(p[2] - pos[2]));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const P = (k) => [
|
|
67
|
+
pos[k * 3],
|
|
68
|
+
pos[k * 3 + 1],
|
|
69
|
+
pos[k * 3 + 2]
|
|
70
|
+
];
|
|
71
|
+
const fn = new Float64Array(nu * nv * 3);
|
|
72
|
+
for (let j = 0; j < nv; j++) {
|
|
73
|
+
for (let i = 0; i < nu; i++) {
|
|
74
|
+
const a = j * W + i;
|
|
75
|
+
const A = P(a), B = P(a + 1), C = P(a + W + 1), D = P(a + W);
|
|
76
|
+
/* the diagonals' cross product survives a quad that pinches to a
|
|
77
|
+
point at a pole */
|
|
78
|
+
let n = cross(sub(C, A), sub(D, B));
|
|
79
|
+
const mid = [
|
|
80
|
+
(A[0] + B[0] + C[0] + D[0]) / 4,
|
|
81
|
+
(A[1] + B[1] + C[1] + D[1]) / 4,
|
|
82
|
+
(A[2] + B[2] + C[2] + D[2]) / 4
|
|
83
|
+
];
|
|
84
|
+
const ins = typeof inside === "function" ? inside((i + .5) / nu, (j + .5) / nv) : inside;
|
|
85
|
+
if (dot(n, sub(mid, ins)) < 0) n = [
|
|
86
|
+
-n[0],
|
|
87
|
+
-n[1],
|
|
88
|
+
-n[2]
|
|
89
|
+
];
|
|
90
|
+
n = norm(n);
|
|
91
|
+
const f = (j * nu + i) * 3;
|
|
92
|
+
fn[f] = n[0];
|
|
93
|
+
fn[f + 1] = n[1];
|
|
94
|
+
fn[f + 2] = n[2];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const eps = 1e-6 + ext * 1e-5;
|
|
98
|
+
const same = (p, q) => Math.abs(pos[p * 3] - pos[q * 3]) + Math.abs(pos[p * 3 + 1] - pos[q * 3 + 1]) + Math.abs(pos[p * 3 + 2] - pos[q * 3 + 2]) < eps;
|
|
99
|
+
let closed = true;
|
|
100
|
+
for (let j = 0; j <= nv && closed; j++) closed = same(j * W, j * W + nu);
|
|
101
|
+
let vn = null;
|
|
102
|
+
if (smooth) {
|
|
103
|
+
const acc = new Float64Array(N * 3);
|
|
104
|
+
for (let j = 0; j < nv; j++) {
|
|
105
|
+
for (let i = 0; i < nu; i++) {
|
|
106
|
+
const f = (j * nu + i) * 3;
|
|
107
|
+
const a = j * W + i;
|
|
108
|
+
for (const k of [
|
|
109
|
+
a,
|
|
110
|
+
a + 1,
|
|
111
|
+
a + W + 1,
|
|
112
|
+
a + W
|
|
113
|
+
]) {
|
|
114
|
+
acc[k * 3] += fn[f];
|
|
115
|
+
acc[k * 3 + 1] += fn[f + 1];
|
|
116
|
+
acc[k * 3 + 2] += fn[f + 2];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const join = (ks) => {
|
|
121
|
+
let x = 0, y = 0, z = 0;
|
|
122
|
+
for (const k of ks) {
|
|
123
|
+
x += acc[k * 3];
|
|
124
|
+
y += acc[k * 3 + 1];
|
|
125
|
+
z += acc[k * 3 + 2];
|
|
126
|
+
}
|
|
127
|
+
for (const k of ks) {
|
|
128
|
+
acc[k * 3] = x;
|
|
129
|
+
acc[k * 3 + 1] = y;
|
|
130
|
+
acc[k * 3 + 2] = z;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
if (closed) for (let j = 0; j <= nv; j++) join([j * W, j * W + nu]);
|
|
134
|
+
for (const j of [0, nv]) {
|
|
135
|
+
let pole = true;
|
|
136
|
+
for (let i = 1; i <= nu && pole; i++) pole = same(j * W, j * W + i);
|
|
137
|
+
if (pole) join(Array.from({ length: W }, (_, i) => j * W + i));
|
|
138
|
+
}
|
|
139
|
+
for (let k = 0; k < N; k++) {
|
|
140
|
+
const x = acc[k * 3], y = acc[k * 3 + 1], z = acc[k * 3 + 2];
|
|
141
|
+
const l = Math.hypot(x, y, z) || 1;
|
|
142
|
+
acc[k * 3] = x / l;
|
|
143
|
+
acc[k * 3 + 1] = y / l;
|
|
144
|
+
acc[k * 3 + 2] = z / l;
|
|
145
|
+
}
|
|
146
|
+
vn = acc;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
nu,
|
|
150
|
+
nv,
|
|
151
|
+
closed,
|
|
152
|
+
pos,
|
|
153
|
+
fn,
|
|
154
|
+
vn
|
|
155
|
+
};
|
|
155
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* A tube along a curve: `path(u)` its centre line, `up` the direction its
|
|
159
|
+
* cross-section is squared to (a band's depth axis, a rim's face normal),
|
|
160
|
+
* `prof(u, φ)` the cross-section's offset at angle φ — along the up
|
|
161
|
+
* direction and across it. Rows of the grid run along the tube.
|
|
162
|
+
*/
|
|
156
163
|
export function makeTube(path, up, prof, nu, nv, closed = false) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
164
|
+
const e = .001;
|
|
165
|
+
const at = (u) => path(closed ? (u % 1 + 1) % 1 : Math.max(0, Math.min(1, u)));
|
|
166
|
+
return makeMesh((u, v) => {
|
|
167
|
+
const c = path(u);
|
|
168
|
+
const T = norm(sub(at(u + e), at(u - e)));
|
|
169
|
+
const k = dot(up, T);
|
|
170
|
+
const Nn = norm([
|
|
171
|
+
up[0] - T[0] * k,
|
|
172
|
+
up[1] - T[1] * k,
|
|
173
|
+
up[2] - T[2] * k
|
|
174
|
+
]);
|
|
175
|
+
const Bn = cross(T, Nn);
|
|
176
|
+
const [a, b] = prof(u, v * TAU);
|
|
177
|
+
return [
|
|
178
|
+
c[0] + Nn[0] * a + Bn[0] * b,
|
|
179
|
+
c[1] + Nn[1] * a + Bn[1] * b,
|
|
180
|
+
c[2] + Nn[2] * a + Bn[2] * b
|
|
181
|
+
];
|
|
182
|
+
}, nu, nv, (u) => path(u));
|
|
176
183
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
};
|
|
184
|
+
/** A cross-section: an ellipse (`p` = 1) squared toward a rounded box as
|
|
185
|
+
`p` falls, `a` along the up direction and `b` across it. */
|
|
186
|
+
export const section = (a, b, p = 1) => (_u, ph) => {
|
|
187
|
+
const c = Math.cos(ph), s = Math.sin(ph);
|
|
188
|
+
return [a * Math.sign(c) * Math.pow(Math.abs(c), p), b * Math.sign(s) * Math.pow(Math.abs(s), p)];
|
|
189
|
+
};
|
|
184
190
|
export function projectMesh(view, mesh) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
191
|
+
const { nu, nv, pos, fn, vn } = mesh;
|
|
192
|
+
const W = nu + 1, N = W * (nv + 1), C = nu * nv;
|
|
193
|
+
const [m0, m1, m2, m3, m4, m5, m6, m7, m8] = view.m;
|
|
194
|
+
const [ox, oy, oz] = view.o;
|
|
195
|
+
const x = new Float64Array(N), y = new Float64Array(N), d = new Float64Array(N);
|
|
196
|
+
for (let k = 0; k < N; k++) {
|
|
197
|
+
const X = pos[k * 3] - ox, Y = pos[k * 3 + 1] - oy, Z = pos[k * 3 + 2] - oz;
|
|
198
|
+
x[k] = m0 * X + m1 * Y + m2 * Z;
|
|
199
|
+
y[k] = m3 * X + m4 * Y + m5 * Z;
|
|
200
|
+
d[k] = m6 * X + m7 * Y + m8 * Z;
|
|
201
|
+
}
|
|
202
|
+
const turn = (src, dst, count) => {
|
|
203
|
+
for (let k = 0; k < count; k++) {
|
|
204
|
+
const a = src[k * 3], b = src[k * 3 + 1], c = src[k * 3 + 2];
|
|
205
|
+
const X = m0 * a + m1 * b + m2 * c, Y = m3 * a + m4 * b + m5 * c, Z = m6 * a + m7 * b + m8 * c;
|
|
206
|
+
const l = Math.hypot(X, Y, Z) || 1;
|
|
207
|
+
dst[k * 3] = X / l;
|
|
208
|
+
dst[k * 3 + 1] = Y / l;
|
|
209
|
+
dst[k * 3 + 2] = Z / l;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
let n = null;
|
|
213
|
+
if (vn) turn(vn, n = new Float64Array(N * 3), N);
|
|
214
|
+
const f = new Float64Array(C * 3), cd = new Float64Array(C);
|
|
215
|
+
turn(fn, f, C);
|
|
216
|
+
for (let j = 0, c = 0; j < nv; j++) {
|
|
217
|
+
for (let i = 0; i < nu; i++, c++) {
|
|
218
|
+
const a = j * W + i;
|
|
219
|
+
cd[c] = (d[a] + d[a + 1] + d[a + W] + d[a + W + 1]) / 4;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
nu,
|
|
224
|
+
nv,
|
|
225
|
+
closed: mesh.closed,
|
|
226
|
+
x,
|
|
227
|
+
y,
|
|
228
|
+
d,
|
|
229
|
+
n,
|
|
230
|
+
f,
|
|
231
|
+
cd
|
|
232
|
+
};
|
|
227
233
|
}
|
|
234
|
+
/** The screen bounds of some projected meshes. */
|
|
228
235
|
export function boundsOf(ps) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
236
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
237
|
+
for (const p of ps) {
|
|
238
|
+
for (let k = 0; k < p.x.length; k++) {
|
|
239
|
+
if (p.x[k] < x0) x0 = p.x[k];
|
|
240
|
+
if (p.x[k] > x1) x1 = p.x[k];
|
|
241
|
+
if (p.y[k] < y0) y0 = p.y[k];
|
|
242
|
+
if (p.y[k] > y1) y1 = p.y[k];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
x0,
|
|
247
|
+
y0,
|
|
248
|
+
x1,
|
|
249
|
+
y1,
|
|
250
|
+
cx: (x0 + x1) / 2,
|
|
251
|
+
cy: (y0 + y1) / 2,
|
|
252
|
+
w: x1 - x0,
|
|
253
|
+
h: y1 - y0
|
|
254
|
+
};
|
|
248
255
|
}
|
|
256
|
+
/* is the run of column midpoints s … e one way along its chord */
|
|
249
257
|
function monotone(mx, my, s, e) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
const ax = mx[e] - mx[s], ay = my[e] - my[s];
|
|
259
|
+
let prev = -Infinity;
|
|
260
|
+
for (let k = s; k <= e; k++) {
|
|
261
|
+
const t = (mx[k] - mx[s]) * ax + (my[k] - my[s]) * ay;
|
|
262
|
+
if (t < prev - 1e-9) return false;
|
|
263
|
+
prev = t;
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
258
266
|
}
|
|
259
|
-
const clampByte = (v)=>v < 0 ? 0 : v > 255 ? 255 : Math.round(v);
|
|
260
|
-
export const css = (c)
|
|
267
|
+
const clampByte = (v) => v < 0 ? 0 : v > 255 ? 255 : Math.round(v);
|
|
268
|
+
export const css = (c) => `rgb(${clampByte(c[0])},${clampByte(c[1])},${clampByte(c[2])})`;
|
|
269
|
+
/** Paint several surfaces as one, back to front, the parts facing away
|
|
270
|
+
dropped. `px` is the view's scale. Returns the corners painted, for an
|
|
271
|
+
outline. */
|
|
261
272
|
export function paintLayers(ctx, layers, px) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
g.addColorStop(off < 0 ? 0 : off > 1 ? 1 : off, css(c));
|
|
406
|
-
last = c;
|
|
407
|
-
}
|
|
408
|
-
style = g;
|
|
409
|
-
}
|
|
410
|
-
ctx.fillStyle = style;
|
|
411
|
-
ctx.beginPath();
|
|
412
|
-
ctx.moveTo(tx[0], ty[0]);
|
|
413
|
-
for(let k = 1; k < nc; k++)ctx.lineTo(tx[k], ty[k]);
|
|
414
|
-
for(let k = nc - 1; k >= 0; k--)ctx.lineTo(bx[k], by[k]);
|
|
415
|
-
ctx.closePath();
|
|
416
|
-
ctx.fill();
|
|
417
|
-
}
|
|
418
|
-
return corners;
|
|
273
|
+
const pieces = [];
|
|
274
|
+
const corners = [];
|
|
275
|
+
for (const l of layers) {
|
|
276
|
+
const bias = l.bias ?? 0, keep = l.keep, cull = l.cull !== false;
|
|
277
|
+
for (const P of l.parts) {
|
|
278
|
+
const { nu, nv, f, cd, x, y } = P, W = nu + 1;
|
|
279
|
+
const vis = (c) => (!cull || f[c * 3 + 2] > -.04) && (!keep || keep(cd[c]));
|
|
280
|
+
for (let j = 0; j < nv; j++) {
|
|
281
|
+
if (l.flat) {
|
|
282
|
+
for (let i = 0; i < nu; i++) {
|
|
283
|
+
const c = j * nu + i;
|
|
284
|
+
if (!vis(c)) continue;
|
|
285
|
+
pieces.push({
|
|
286
|
+
d: cd[c] + bias,
|
|
287
|
+
l,
|
|
288
|
+
p: P,
|
|
289
|
+
j,
|
|
290
|
+
cols: [i, i + 1],
|
|
291
|
+
cells: [c]
|
|
292
|
+
});
|
|
293
|
+
corners.push([x[j * W + i], y[j * W + i]], [x[(j + 1) * W + i + 1], y[(j + 1) * W + i + 1]]);
|
|
294
|
+
}
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
/* the row's visible runs, joined across the seam of a closed grid */
|
|
298
|
+
const runs = [];
|
|
299
|
+
let cur = null;
|
|
300
|
+
for (let i = 0; i < nu; i++) {
|
|
301
|
+
if (vis(j * nu + i)) {
|
|
302
|
+
if (!cur) runs.push(cur = []);
|
|
303
|
+
cur.push(i);
|
|
304
|
+
} else cur = null;
|
|
305
|
+
}
|
|
306
|
+
if (P.closed && runs.length > 1 && runs[0][0] === 0 && runs[runs.length - 1][runs[runs.length - 1].length - 1] === nu - 1) {
|
|
307
|
+
runs[0] = runs.pop().concat(runs[0]);
|
|
308
|
+
}
|
|
309
|
+
for (const run of runs) {
|
|
310
|
+
const cols = run.concat(run[run.length - 1] + 1);
|
|
311
|
+
const mx = [], my = [];
|
|
312
|
+
for (let k = 0; k < cols.length; k++) {
|
|
313
|
+
const t = j * W + cols[k], b = t + W;
|
|
314
|
+
mx.push((x[t] + x[b]) / 2);
|
|
315
|
+
my.push((y[t] + y[b]) / 2);
|
|
316
|
+
/* the outline needs only the silhouette: a run's ends, and the
|
|
317
|
+
grid's first and last rows */
|
|
318
|
+
if (k === 0 || k === cols.length - 1) corners.push([x[t], y[t]], [x[b], y[b]]);
|
|
319
|
+
else if (j === 0) corners.push([x[t], y[t]]);
|
|
320
|
+
else if (j === nv - 1) corners.push([x[b], y[b]]);
|
|
321
|
+
}
|
|
322
|
+
/* split where the row turns back on itself on screen, so each
|
|
323
|
+
strip can take one linear gradient */
|
|
324
|
+
let s = 0;
|
|
325
|
+
while (s < cols.length - 1) {
|
|
326
|
+
let e = s + 1;
|
|
327
|
+
while (e + 1 < cols.length && monotone(mx, my, s, e + 1)) e++;
|
|
328
|
+
let dd = 0;
|
|
329
|
+
for (let k = s; k < e; k++) dd += cd[j * nu + run[k]];
|
|
330
|
+
pieces.push({
|
|
331
|
+
d: dd / (e - s) + bias,
|
|
332
|
+
l,
|
|
333
|
+
p: P,
|
|
334
|
+
j,
|
|
335
|
+
cols: cols.slice(s, e + 1),
|
|
336
|
+
cells: run.slice(s, e)
|
|
337
|
+
});
|
|
338
|
+
s = e;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
pieces.sort((a, b) => a.d - b.d);
|
|
345
|
+
const grow = .5 / px;
|
|
346
|
+
for (const pc of pieces) {
|
|
347
|
+
const { p: P, l, j, cols, cells } = pc;
|
|
348
|
+
const { x, y, n, f } = P, W = P.nu + 1;
|
|
349
|
+
const nc = cols.length;
|
|
350
|
+
/* the strip's outline, pushed out a little all round so neighbours
|
|
351
|
+
overlap and no seam of background shows */
|
|
352
|
+
const tx = [], ty = [], bx = [], by = [];
|
|
353
|
+
const t0 = j * W + cols[0], tn = j * W + cols[nc - 1];
|
|
354
|
+
let ax = (x[tn] + x[tn + W]) / 2 - (x[t0] + x[t0 + W]) / 2, ay = (y[tn] + y[tn + W]) / 2 - (y[t0] + y[t0 + W]) / 2;
|
|
355
|
+
const al = Math.hypot(ax, ay);
|
|
356
|
+
const ux = al > 1e-9 ? ax / al : 0, uy = al > 1e-9 ? ay / al : 0;
|
|
357
|
+
for (let k = 0; k < nc; k++) {
|
|
358
|
+
const t = j * W + cols[k], b = t + W;
|
|
359
|
+
let dx = x[t] - x[b], dy = y[t] - y[b];
|
|
360
|
+
const dl = Math.hypot(dx, dy);
|
|
361
|
+
if (dl > 1e-9) dx /= dl, dy /= dl;
|
|
362
|
+
else dx = dy = 0;
|
|
363
|
+
const e = k === 0 ? -grow : k === nc - 1 ? grow : 0;
|
|
364
|
+
tx.push(x[t] + dx * grow + ux * e);
|
|
365
|
+
ty.push(y[t] + dy * grow + uy * e);
|
|
366
|
+
bx.push(x[b] - dx * grow + ux * e);
|
|
367
|
+
by.push(y[b] - dy * grow + uy * e);
|
|
368
|
+
}
|
|
369
|
+
/* the colour at each column: from the corners' normals, or the cells'
|
|
370
|
+
beside it on a faceted mesh; stops only where the colour has moved */
|
|
371
|
+
const colour = (k) => {
|
|
372
|
+
if (n && !l.flat) {
|
|
373
|
+
const t = (j * W + cols[k]) * 3, b = t + W * 3;
|
|
374
|
+
return l.shade(norm([
|
|
375
|
+
n[t] + n[b],
|
|
376
|
+
n[t + 1] + n[b + 1],
|
|
377
|
+
n[t + 2] + n[b + 2]
|
|
378
|
+
]));
|
|
379
|
+
}
|
|
380
|
+
const c0 = cells[Math.max(0, Math.min(cells.length - 1, k - 1))] * 3, c1 = cells[Math.min(cells.length - 1, k)] * 3;
|
|
381
|
+
return l.shade(l.flat ? [
|
|
382
|
+
f[c1],
|
|
383
|
+
f[c1 + 1],
|
|
384
|
+
f[c1 + 2]
|
|
385
|
+
] : norm([
|
|
386
|
+
f[c0] + f[c1],
|
|
387
|
+
f[c0 + 1] + f[c1 + 1],
|
|
388
|
+
f[c0 + 2] + f[c1 + 2]
|
|
389
|
+
]));
|
|
390
|
+
};
|
|
391
|
+
let style;
|
|
392
|
+
if (l.flat || al < 1e-6) style = css(colour(0));
|
|
393
|
+
else {
|
|
394
|
+
const g = ctx.createLinearGradient((x[t0] + x[t0 + W]) / 2, (y[t0] + y[t0 + W]) / 2, (x[tn] + x[tn + W]) / 2, (y[tn] + y[tn + W]) / 2);
|
|
395
|
+
let last = null;
|
|
396
|
+
const m0x = (x[t0] + x[t0 + W]) / 2, m0y = (y[t0] + y[t0 + W]) / 2;
|
|
397
|
+
for (let k = 0; k < nc; k++) {
|
|
398
|
+
const c = colour(k);
|
|
399
|
+
if (last && k < nc - 1 && Math.abs(c[0] - last[0]) + Math.abs(c[1] - last[1]) + Math.abs(c[2] - last[2]) < 5) continue;
|
|
400
|
+
const t = j * W + cols[k];
|
|
401
|
+
const off = (((x[t] + x[t + W]) / 2 - m0x) * ax + ((y[t] + y[t + W]) / 2 - m0y) * ay) / (al * al);
|
|
402
|
+
g.addColorStop(off < 0 ? 0 : off > 1 ? 1 : off, css(c));
|
|
403
|
+
last = c;
|
|
404
|
+
}
|
|
405
|
+
style = g;
|
|
406
|
+
}
|
|
407
|
+
ctx.fillStyle = style;
|
|
408
|
+
ctx.beginPath();
|
|
409
|
+
ctx.moveTo(tx[0], ty[0]);
|
|
410
|
+
for (let k = 1; k < nc; k++) ctx.lineTo(tx[k], ty[k]);
|
|
411
|
+
for (let k = nc - 1; k >= 0; k--) ctx.lineTo(bx[k], by[k]);
|
|
412
|
+
ctx.closePath();
|
|
413
|
+
ctx.fill();
|
|
414
|
+
}
|
|
415
|
+
return corners;
|
|
419
416
|
}
|
|
420
417
|
export function hull(pts) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
418
|
+
const p = pts.slice().sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
419
|
+
if (p.length < 3) return p;
|
|
420
|
+
const cr = (o, a, b) => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
|
|
421
|
+
const lo = [], up = [];
|
|
422
|
+
for (const q of p) {
|
|
423
|
+
while (lo.length >= 2 && cr(lo[lo.length - 2], lo[lo.length - 1], q) <= 0) lo.pop();
|
|
424
|
+
lo.push(q);
|
|
425
|
+
}
|
|
426
|
+
for (let i = p.length - 1; i >= 0; i--) {
|
|
427
|
+
const q = p[i];
|
|
428
|
+
while (up.length >= 2 && cr(up[up.length - 2], up[up.length - 1], q) <= 0) up.pop();
|
|
429
|
+
up.push(q);
|
|
430
|
+
}
|
|
431
|
+
lo.pop();
|
|
432
|
+
up.pop();
|
|
433
|
+
return lo.concat(up);
|
|
437
434
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
435
|
+
/* ── materials: a view and a base colour to a shade ────────────────── */
|
|
436
|
+
const clamp01 = (x) => x < 0 ? 0 : x > 1 ? 1 : x;
|
|
437
|
+
export const mix = (a, b, t) => [
|
|
438
|
+
a[0] + (b[0] - a[0]) * t,
|
|
439
|
+
a[1] + (b[1] - a[1]) * t,
|
|
440
|
+
a[2] + (b[2] - a[2]) * t
|
|
441
|
+
];
|
|
442
|
+
export const scale = (a, k) => [
|
|
443
|
+
a[0] * k,
|
|
444
|
+
a[1] * k,
|
|
445
|
+
a[2] * k
|
|
446
|
+
];
|
|
449
447
|
const WHITE = [
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
448
|
+
255,
|
|
449
|
+
255,
|
|
450
|
+
255
|
|
453
451
|
];
|
|
452
|
+
/** Felt and knit: matte, the light wrapping softly round, a faint sheen
|
|
453
|
+
where the fibres lie side-on; a dark felt keeps a floor of light so it
|
|
454
|
+
reads charcoal on its lit side. `k` darkens it (a fold, an underside). */
|
|
454
455
|
export function felt(view, base, k = 1) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
456
|
+
const L = view.L;
|
|
457
|
+
return (n) => {
|
|
458
|
+
const nl = dot(n, L);
|
|
459
|
+
const dif = Math.pow(clamp01((nl + .4) / 1.4), 1.35);
|
|
460
|
+
const graze = Math.pow(1 - clamp01(n[2]), 2);
|
|
461
|
+
const lit = (.22 + .95 * dif + .22 * graze * clamp01(nl + .6)) * k;
|
|
462
|
+
const floor = 26 * dif * k;
|
|
463
|
+
return [
|
|
464
|
+
base[0] * lit + floor,
|
|
465
|
+
base[1] * lit + floor,
|
|
466
|
+
base[2] * lit + floor * 1.05
|
|
467
|
+
];
|
|
468
|
+
};
|
|
468
469
|
}
|
|
470
|
+
/** Velvet: deep in its folds, its colour lifting toward the silhouette. */
|
|
469
471
|
export function velvet(view, base) {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
472
|
+
const L = view.L, sheen = mix(base, WHITE, .3);
|
|
473
|
+
return (n) => {
|
|
474
|
+
const nl = dot(n, L);
|
|
475
|
+
const dif = clamp01((nl + .3) / 1.3);
|
|
476
|
+
const graze = Math.pow(1 - clamp01(n[2]), 1.6);
|
|
477
|
+
return mix(scale(base, .2 + .7 * dif), sheen, .6 * graze * (.4 + .6 * dif));
|
|
478
|
+
};
|
|
477
479
|
}
|
|
480
|
+
/** Paper: Lambert with a soft wide sheen. */
|
|
478
481
|
export function paper(view, base) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
482
|
+
const L = view.L, H = view.H;
|
|
483
|
+
return (n) => {
|
|
484
|
+
const dif = clamp01((dot(n, L) + .2) / 1.2);
|
|
485
|
+
const spec = Math.pow(clamp01(dot(n, H)), 22) * .3;
|
|
486
|
+
return mix(scale(base, .36 + .7 * dif), WHITE, spec);
|
|
487
|
+
};
|
|
485
488
|
}
|
|
489
|
+
/** Soft plastic and leather: diffuse, a broad sheen and a tighter hot
|
|
490
|
+
spot, a thin rim of light at the silhouette. */
|
|
486
491
|
export function plastic(view, base, gloss = 1) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
492
|
+
const L = view.L, H = view.H;
|
|
493
|
+
return (n) => {
|
|
494
|
+
const dif = clamp01((dot(n, L) + .25) / 1.25);
|
|
495
|
+
const nh = clamp01(dot(n, H));
|
|
496
|
+
const spec = (Math.pow(nh, 16) * .2 + Math.pow(nh, 80) * .6) * gloss;
|
|
497
|
+
const rim = Math.pow(1 - clamp01(n[2]), 3) * .14 * gloss;
|
|
498
|
+
return mix(scale(base, .28 + .78 * dif), WHITE, clamp01(spec + rim));
|
|
499
|
+
};
|
|
495
500
|
}
|
|
501
|
+
/** Satin: a silk ribbon's light — a wide soft sheen that runs along the
|
|
502
|
+
folds and a deep, saturated shade between them. */
|
|
496
503
|
export function satin(view, base) {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
+
const L = view.L, H = view.H, deep = scale(base, .42), sheen = mix(base, WHITE, .55);
|
|
505
|
+
return (n) => {
|
|
506
|
+
const dif = clamp01((dot(n, L) + .35) / 1.35);
|
|
507
|
+
const nh = clamp01(dot(n, H));
|
|
508
|
+
const s = Math.pow(nh, 6) * .5 + Math.pow(nh, 40) * .35;
|
|
509
|
+
return mix(mix(deep, base, dif), sheen, clamp01(s));
|
|
510
|
+
};
|
|
504
511
|
}
|
|
512
|
+
/** Metal: the colour of what it reflects — a bright sky above, a dark
|
|
513
|
+
floor below, a bright horizon between — tinted, with a hot glint. */
|
|
505
514
|
export function metal(view, tint, dark, glint) {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
515
|
+
const H = view.H, L = view.L;
|
|
516
|
+
return (n) => {
|
|
517
|
+
/* the reflection of the view about the normal; up is −y */
|
|
518
|
+
const rz = 2 * n[2] * n[2] - 1, ry = 2 * n[2] * n[1];
|
|
519
|
+
const up = -ry;
|
|
520
|
+
const sky = clamp01(.45 + up * .85 + rz * .1);
|
|
521
|
+
const horizon = Math.exp(-Math.pow((up + .12) / .16, 2)) * .55;
|
|
522
|
+
const nl = clamp01(dot(n, L));
|
|
523
|
+
const c = mix(dark, tint, clamp01(.1 + sky * .72 + nl * .3));
|
|
524
|
+
const spec = Math.pow(clamp01(dot(n, H)), 70) + horizon * .4;
|
|
525
|
+
return mix(c, glint, clamp01(spec));
|
|
526
|
+
};
|
|
517
527
|
}
|
|
518
|
-
export const gold = (view)=>metal(view, [
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
export const silver = (view)=>metal(view, [
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
528
|
+
export const gold = (view) => metal(view, [
|
|
529
|
+
255,
|
|
530
|
+
200,
|
|
531
|
+
88
|
|
532
|
+
], [
|
|
533
|
+
86,
|
|
534
|
+
50,
|
|
535
|
+
8
|
|
536
|
+
], [
|
|
537
|
+
255,
|
|
538
|
+
247,
|
|
539
|
+
220
|
|
540
|
+
]);
|
|
541
|
+
export const silver = (view) => metal(view, [
|
|
542
|
+
232,
|
|
543
|
+
236,
|
|
544
|
+
242
|
|
545
|
+
], [
|
|
546
|
+
58,
|
|
547
|
+
62,
|
|
548
|
+
72
|
|
549
|
+
], [
|
|
550
|
+
255,
|
|
551
|
+
255,
|
|
552
|
+
255
|
|
553
|
+
]);
|
|
554
|
+
/** A pearl: soft white with a pink-blue play and a small bright glint. */
|
|
544
555
|
export function pearl(view) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
556
|
+
const L = view.L, H = view.H;
|
|
557
|
+
return (n) => {
|
|
558
|
+
const dif = clamp01((dot(n, L) + .4) / 1.4);
|
|
559
|
+
const base = mix([
|
|
560
|
+
238,
|
|
561
|
+
228,
|
|
562
|
+
228
|
|
563
|
+
], [
|
|
564
|
+
212,
|
|
565
|
+
222,
|
|
566
|
+
242
|
|
567
|
+
], 1 - clamp01(n[2]));
|
|
568
|
+
return mix(scale(base, .62 + .42 * dif), WHITE, Math.pow(clamp01(dot(n, H)), 40) * .8);
|
|
569
|
+
};
|
|
559
570
|
}
|
|
571
|
+
/** A cut gem's facet: its colour deepening toward the edges, the facets
|
|
572
|
+
turned to the light bright, a white fire on the one that catches it. */
|
|
560
573
|
export function gem(view, base) {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
574
|
+
const L = view.L, H = view.H, deep = scale(base, .22), clear = mix(base, WHITE, .3);
|
|
575
|
+
return (n) => {
|
|
576
|
+
const c = mix(deep, clear, clamp01(clamp01(n[2]) * .75 + dot(n, L) * .4));
|
|
577
|
+
return mix(c, WHITE, Math.pow(clamp01(dot(n, H)), 24) * .95);
|
|
578
|
+
};
|
|
566
579
|
}
|
|
567
580
|
const SPRITE = {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
"#8c5c18"
|
|
592
|
-
]
|
|
593
|
-
],
|
|
594
|
-
silver: [
|
|
595
|
-
[
|
|
596
|
-
0,
|
|
597
|
-
"#ffffff"
|
|
598
|
-
],
|
|
599
|
-
[
|
|
600
|
-
0.08,
|
|
601
|
-
"#f3f5f9"
|
|
602
|
-
],
|
|
603
|
-
[
|
|
604
|
-
0.32,
|
|
605
|
-
"#c6ccd6"
|
|
606
|
-
],
|
|
607
|
-
[
|
|
608
|
-
0.64,
|
|
609
|
-
"#7a808c"
|
|
610
|
-
],
|
|
611
|
-
[
|
|
612
|
-
0.88,
|
|
613
|
-
"#3d4149"
|
|
614
|
-
],
|
|
615
|
-
[
|
|
616
|
-
1,
|
|
617
|
-
"#6b717c"
|
|
618
|
-
]
|
|
619
|
-
],
|
|
620
|
-
pearl: [
|
|
621
|
-
[
|
|
622
|
-
0,
|
|
623
|
-
"#ffffff"
|
|
624
|
-
],
|
|
625
|
-
[
|
|
626
|
-
0.1,
|
|
627
|
-
"#fdf9f7"
|
|
628
|
-
],
|
|
629
|
-
[
|
|
630
|
-
0.45,
|
|
631
|
-
"#ebe4e6"
|
|
632
|
-
],
|
|
633
|
-
[
|
|
634
|
-
0.8,
|
|
635
|
-
"#c6bccb"
|
|
636
|
-
],
|
|
637
|
-
[
|
|
638
|
-
1,
|
|
639
|
-
"#a89db5"
|
|
640
|
-
]
|
|
641
|
-
]
|
|
581
|
+
gold: [
|
|
582
|
+
[0, "#fffbe8"],
|
|
583
|
+
[.08, "#ffe7a0"],
|
|
584
|
+
[.3, "#eeb546"],
|
|
585
|
+
[.62, "#ad7214"],
|
|
586
|
+
[.87, "#5a3806"],
|
|
587
|
+
[1, "#8c5c18"]
|
|
588
|
+
],
|
|
589
|
+
silver: [
|
|
590
|
+
[0, "#ffffff"],
|
|
591
|
+
[.08, "#f3f5f9"],
|
|
592
|
+
[.32, "#c6ccd6"],
|
|
593
|
+
[.64, "#7a808c"],
|
|
594
|
+
[.88, "#3d4149"],
|
|
595
|
+
[1, "#6b717c"]
|
|
596
|
+
],
|
|
597
|
+
pearl: [
|
|
598
|
+
[0, "#ffffff"],
|
|
599
|
+
[.1, "#fdf9f7"],
|
|
600
|
+
[.45, "#ebe4e6"],
|
|
601
|
+
[.8, "#c6bccb"],
|
|
602
|
+
[1, "#a89db5"]
|
|
603
|
+
]
|
|
642
604
|
};
|
|
605
|
+
/** A small sphere as one radial fill: its hot spot where the normal meets
|
|
606
|
+
the half vector, falling to a dark edge with a little reflected light. */
|
|
643
607
|
export function sphereSprite(ctx, view, c, rad, kind) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
608
|
+
const s = view.toScreen(c);
|
|
609
|
+
const H = view.H;
|
|
610
|
+
const g = ctx.createRadialGradient(s[0] + H[0] * rad * .85, s[1] + H[1] * rad * .85, 0, s[0], s[1], rad);
|
|
611
|
+
if (typeof kind === "string") for (const [t, col] of SPRITE[kind]) g.addColorStop(t, col);
|
|
612
|
+
else {
|
|
613
|
+
g.addColorStop(0, css(mix(kind, WHITE, .8)));
|
|
614
|
+
g.addColorStop(.1, css(mix(kind, WHITE, .3)));
|
|
615
|
+
g.addColorStop(.35, css(kind));
|
|
616
|
+
g.addColorStop(.85, css(scale(kind, .5)));
|
|
617
|
+
g.addColorStop(1, css(scale(kind, .62)));
|
|
618
|
+
}
|
|
619
|
+
ctx.fillStyle = g;
|
|
620
|
+
ctx.beginPath();
|
|
621
|
+
ctx.arc(s[0], s[1], rad, 0, TAU);
|
|
622
|
+
ctx.fill();
|
|
659
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* A cut stone set in a surface: `c` its centre, `N` the surface's outward
|
|
626
|
+
* normal, `T` a tangent (object space, unit). An eight-sided girdle, a
|
|
627
|
+
* table on top and the crown facets between, each lit by its own normal.
|
|
628
|
+
*/
|
|
660
629
|
export function cutGem(ctx, view, c, N, T, rad, col) {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
630
|
+
const B = cross(N, T);
|
|
631
|
+
const at = (r, a, h) => {
|
|
632
|
+
const x = r * Math.cos(a), y = r * Math.sin(a);
|
|
633
|
+
return [
|
|
634
|
+
c[0] + x * T[0] + y * B[0] + h * N[0],
|
|
635
|
+
c[1] + x * T[1] + y * B[1] + h * N[1],
|
|
636
|
+
c[2] + x * T[2] + y * B[2] + h * N[2]
|
|
637
|
+
];
|
|
638
|
+
};
|
|
639
|
+
const K = 8, tr = .55, th = .36 * rad;
|
|
640
|
+
const girdle = [], table = [];
|
|
641
|
+
for (let k = 0; k < K; k++) {
|
|
642
|
+
const a = (k + .5) / K * TAU;
|
|
643
|
+
girdle.push(at(rad, a, 0));
|
|
644
|
+
table.push(at(rad * tr, a, th));
|
|
645
|
+
}
|
|
646
|
+
const shade = gem(view, col);
|
|
647
|
+
const poly = (pts, n) => {
|
|
648
|
+
const nv = view.dir(n);
|
|
649
|
+
if (nv[2] <= .02) return;
|
|
650
|
+
ctx.fillStyle = css(shade(nv));
|
|
651
|
+
ctx.beginPath();
|
|
652
|
+
pts.forEach((p, i) => {
|
|
653
|
+
const s = view.toScreen(p);
|
|
654
|
+
if (i) ctx.lineTo(s[0], s[1]);
|
|
655
|
+
else ctx.moveTo(s[0], s[1]);
|
|
656
|
+
});
|
|
657
|
+
ctx.closePath();
|
|
658
|
+
ctx.fill();
|
|
659
|
+
ctx.strokeStyle = ctx.fillStyle;
|
|
660
|
+
ctx.stroke();
|
|
661
|
+
};
|
|
662
|
+
ctx.lineWidth = Math.min(.3, 1 / view.px);
|
|
663
|
+
ctx.lineJoin = "round";
|
|
664
|
+
for (let k = 0; k < K; k++) {
|
|
665
|
+
const g0 = girdle[k], g1 = girdle[(k + 1) % K], t1 = table[(k + 1) % K], t0 = table[k];
|
|
666
|
+
let n = cross(sub(t1, g0), sub(t0, g1));
|
|
667
|
+
if (dot(n, N) < 0) n = [
|
|
668
|
+
-n[0],
|
|
669
|
+
-n[1],
|
|
670
|
+
-n[2]
|
|
671
|
+
];
|
|
672
|
+
poly([
|
|
673
|
+
g0,
|
|
674
|
+
g1,
|
|
675
|
+
t1,
|
|
676
|
+
t0
|
|
677
|
+
], norm(n));
|
|
678
|
+
}
|
|
679
|
+
poly(table, N);
|
|
680
|
+
/* the table's inner light: a lighter kite toward the light */
|
|
681
|
+
const nv = view.dir(N);
|
|
682
|
+
if (nv[2] > .1) {
|
|
683
|
+
const s0 = view.toScreen(at(0, 0, th));
|
|
684
|
+
const L = view.L;
|
|
685
|
+
ctx.fillStyle = "rgba(255,255,255,0.3)";
|
|
686
|
+
ctx.beginPath();
|
|
687
|
+
const s1 = view.toScreen(at(rad * tr * .8, Math.atan2(-L[1], -L[0]) + 2.2, th));
|
|
688
|
+
const s2 = view.toScreen(at(rad * tr * .9, Math.atan2(-L[1], -L[0]) + Math.PI, th));
|
|
689
|
+
const s3 = view.toScreen(at(rad * tr * .8, Math.atan2(-L[1], -L[0]) + Math.PI + .94, th));
|
|
690
|
+
ctx.moveTo(s0[0], s0[1]);
|
|
691
|
+
ctx.lineTo(s1[0], s1[1]);
|
|
692
|
+
ctx.lineTo(s2[0], s2[1]);
|
|
693
|
+
ctx.lineTo(s3[0], s3[1]);
|
|
694
|
+
ctx.closePath();
|
|
695
|
+
ctx.fill();
|
|
696
|
+
}
|
|
727
697
|
}
|