@gradio/model3d 0.16.0 → 0.16.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/CHANGELOG.md +27 -0
- package/Index.svelte +6 -6
- package/dist/Index.svelte +6 -6
- package/dist/shared/Canvas3D.svelte +27 -12
- package/dist/shared/Canvas3D.svelte.d.ts +5 -21
- package/dist/shared/Canvas3DGS.svelte +20 -12
- package/dist/shared/Canvas3DGS.svelte.d.ts +4 -18
- package/dist/shared/Model3D.svelte +45 -34
- package/dist/shared/Model3D.svelte.d.ts +4 -18
- package/dist/shared/Model3DUpload.svelte +87 -65
- package/dist/shared/Model3DUpload.svelte.d.ts +24 -24
- package/package.json +9 -9
- package/shared/Canvas3D.svelte +27 -12
- package/shared/Canvas3DGS.svelte +20 -12
- package/shared/Model3D.svelte +45 -34
- package/shared/Model3DUpload.svelte +87 -65
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @gradio/model3d
|
|
2
2
|
|
|
3
|
+
## 0.16.2
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- [#12800](https://github.com/gradio-app/gradio/pull/12800) [`7a1c321`](https://github.com/gradio-app/gradio/commit/7a1c321b6546ba05a353488f5133e8262c4a8a39) - Bump svelte/kit for security reasons. Thanks @freddyaboulton!
|
|
8
|
+
- [#12779](https://github.com/gradio-app/gradio/pull/12779) [`ea2d3e9`](https://github.com/gradio-app/gradio/commit/ea2d3e985a8b42d188e551f517c5825c00790628) - Migrate Audio + Upload + Atoms to Svelte 5. Thanks @dawoodkhan82!
|
|
9
|
+
|
|
10
|
+
### Dependency updates
|
|
11
|
+
|
|
12
|
+
- @gradio/statustracker@0.12.2
|
|
13
|
+
- @gradio/atoms@0.20.1
|
|
14
|
+
- @gradio/utils@0.11.2
|
|
15
|
+
- @gradio/icons@0.15.1
|
|
16
|
+
- @gradio/upload@0.17.4
|
|
17
|
+
- @gradio/client@2.0.3
|
|
18
|
+
|
|
19
|
+
## 0.16.1
|
|
20
|
+
|
|
21
|
+
### Fixes
|
|
22
|
+
|
|
23
|
+
- [#12688](https://github.com/gradio-app/gradio/pull/12688) [`5206d47`](https://github.com/gradio-app/gradio/commit/5206d47b7730e6ffc8efd90d8157dd5388c18dc5) - Migrate Model3D to Svelte 5. Thanks @dawoodkhan82!
|
|
24
|
+
|
|
25
|
+
### Dependency updates
|
|
26
|
+
|
|
27
|
+
- @gradio/utils@0.11.1
|
|
28
|
+
- @gradio/client@2.0.2
|
|
29
|
+
|
|
3
30
|
## 0.16.0
|
|
4
31
|
|
|
5
32
|
### Features
|
package/Index.svelte
CHANGED
|
@@ -157,15 +157,15 @@
|
|
|
157
157
|
root={gradio.shared.root}
|
|
158
158
|
display_mode={gradio.props.display_mode}
|
|
159
159
|
clear_color={gradio.props.clear_color}
|
|
160
|
-
value={gradio.props.value}
|
|
160
|
+
bind:value={gradio.props.value}
|
|
161
161
|
camera_position={gradio.props.camera_position}
|
|
162
162
|
zoom_speed={gradio.props.zoom_speed}
|
|
163
163
|
bind:uploading
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
onchange={handle_change}
|
|
165
|
+
ondrag={handle_drag}
|
|
166
|
+
onclear={handle_clear}
|
|
167
|
+
onload={handle_load}
|
|
168
|
+
onerror={handle_error}
|
|
169
169
|
i18n={gradio.i18n}
|
|
170
170
|
max_file_size={gradio.shared.max_file_size}
|
|
171
171
|
upload={(...args) => gradio.shared.client.upload(...args)}
|
package/dist/Index.svelte
CHANGED
|
@@ -157,15 +157,15 @@
|
|
|
157
157
|
root={gradio.shared.root}
|
|
158
158
|
display_mode={gradio.props.display_mode}
|
|
159
159
|
clear_color={gradio.props.clear_color}
|
|
160
|
-
value={gradio.props.value}
|
|
160
|
+
bind:value={gradio.props.value}
|
|
161
161
|
camera_position={gradio.props.camera_position}
|
|
162
162
|
zoom_speed={gradio.props.zoom_speed}
|
|
163
163
|
bind:uploading
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
onchange={handle_change}
|
|
165
|
+
ondrag={handle_drag}
|
|
166
|
+
onclear={handle_clear}
|
|
167
|
+
onload={handle_load}
|
|
168
|
+
onerror={handle_error}
|
|
169
169
|
i18n={gradio.i18n}
|
|
170
170
|
max_file_size={gradio.shared.max_file_size}
|
|
171
171
|
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
@@ -5,19 +5,28 @@
|
|
|
5
5
|
|
|
6
6
|
let BABYLON_VIEWER: typeof import("@babylonjs/viewer");
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
let {
|
|
9
|
+
value,
|
|
10
|
+
display_mode,
|
|
11
|
+
clear_color,
|
|
12
|
+
camera_position,
|
|
13
|
+
zoom_speed,
|
|
14
|
+
pan_speed
|
|
15
|
+
}: {
|
|
16
|
+
value: FileData;
|
|
17
|
+
display_mode: "solid" | "point_cloud" | "wireframe";
|
|
18
|
+
clear_color: [number, number, number, number];
|
|
19
|
+
camera_position: [number | null, number | null, number | null];
|
|
20
|
+
zoom_speed: number;
|
|
21
|
+
pan_speed: number;
|
|
22
|
+
} = $props();
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
let url = $derived(value.url);
|
|
16
25
|
|
|
17
26
|
let canvas: HTMLCanvasElement;
|
|
18
|
-
let viewer
|
|
19
|
-
let viewerDetails
|
|
20
|
-
let mounted = false;
|
|
27
|
+
let viewer = $state<Viewer>();
|
|
28
|
+
let viewerDetails = $state<Readonly<ViewerDetails>>();
|
|
29
|
+
let mounted = $state(false);
|
|
21
30
|
|
|
22
31
|
onMount(() => {
|
|
23
32
|
const initViewer = async (): Promise<void> => {
|
|
@@ -43,9 +52,14 @@
|
|
|
43
52
|
};
|
|
44
53
|
});
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
$effect(() => {
|
|
56
|
+
if (mounted) {
|
|
57
|
+
load_model(url);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
47
60
|
|
|
48
61
|
function setRenderingMode(pointsCloud: boolean, wireframe: boolean): void {
|
|
62
|
+
if (!viewerDetails) return;
|
|
49
63
|
viewerDetails.scene.forcePointsCloud = pointsCloud;
|
|
50
64
|
viewerDetails.scene.forceWireframe = wireframe;
|
|
51
65
|
}
|
|
@@ -81,6 +95,7 @@
|
|
|
81
95
|
zoom_speed: number,
|
|
82
96
|
pan_speed: number
|
|
83
97
|
): void {
|
|
98
|
+
if (!viewerDetails) return;
|
|
84
99
|
const camera = viewerDetails.camera;
|
|
85
100
|
if (camera_position[0] !== null) {
|
|
86
101
|
camera.alpha = (camera_position[0] * Math.PI) / 180;
|
|
@@ -101,7 +116,7 @@
|
|
|
101
116
|
}
|
|
102
117
|
|
|
103
118
|
export function reset_camera_position(): void {
|
|
104
|
-
if (viewerDetails) {
|
|
119
|
+
if (viewerDetails && viewer) {
|
|
105
120
|
viewer.resetCamera();
|
|
106
121
|
}
|
|
107
122
|
}
|
|
@@ -1,31 +1,15 @@
|
|
|
1
1
|
import type { FileData } from "@gradio/client";
|
|
2
|
-
|
|
3
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
-
$$bindings?: Bindings;
|
|
5
|
-
} & Exports;
|
|
6
|
-
(internal: unknown, props: Props & {
|
|
7
|
-
$$events?: Events;
|
|
8
|
-
$$slots?: Slots;
|
|
9
|
-
}): Exports & {
|
|
10
|
-
$set?: any;
|
|
11
|
-
$on?: any;
|
|
12
|
-
};
|
|
13
|
-
z_$$bindings?: Bindings;
|
|
14
|
-
}
|
|
15
|
-
declare const Canvas3D: $$__sveltets_2_IsomorphicComponent<{
|
|
2
|
+
type $$ComponentProps = {
|
|
16
3
|
value: FileData;
|
|
17
4
|
display_mode: "solid" | "point_cloud" | "wireframe";
|
|
18
5
|
clear_color: [number, number, number, number];
|
|
19
6
|
camera_position: [number | null, number | null, number | null];
|
|
20
7
|
zoom_speed: number;
|
|
21
8
|
pan_speed: number;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}, {
|
|
25
|
-
[evt: string]: CustomEvent<any>;
|
|
26
|
-
}, {}, {
|
|
9
|
+
};
|
|
10
|
+
declare const Canvas3D: import("svelte").Component<$$ComponentProps, {
|
|
27
11
|
update_camera: (camera_position: [number | null, number | null, number | null], zoom_speed: number, pan_speed: number) => void;
|
|
28
12
|
reset_camera_position: () => void;
|
|
29
|
-
},
|
|
30
|
-
type Canvas3D =
|
|
13
|
+
}, "">;
|
|
14
|
+
type Canvas3D = ReturnType<typeof Canvas3D>;
|
|
31
15
|
export default Canvas3D;
|
|
@@ -3,19 +3,25 @@
|
|
|
3
3
|
import * as SPLAT from "gsplat";
|
|
4
4
|
import type { FileData } from "@gradio/client";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
let {
|
|
7
|
+
value,
|
|
8
|
+
zoom_speed,
|
|
9
|
+
pan_speed
|
|
10
|
+
}: {
|
|
11
|
+
value: FileData;
|
|
12
|
+
zoom_speed: number;
|
|
13
|
+
pan_speed: number;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
let url = $derived(value.url);
|
|
11
17
|
|
|
12
18
|
let canvas: HTMLCanvasElement;
|
|
13
19
|
let scene: SPLAT.Scene;
|
|
14
20
|
let camera: SPLAT.Camera;
|
|
15
|
-
let renderer
|
|
21
|
+
let renderer = $state<SPLAT.WebGLRenderer | null>(null);
|
|
16
22
|
let controls: SPLAT.OrbitControls;
|
|
17
|
-
let mounted = false;
|
|
18
|
-
let frameId
|
|
23
|
+
let mounted = $state(false);
|
|
24
|
+
let frameId = $state<number | null>(null);
|
|
19
25
|
|
|
20
26
|
function reset_scene(): void {
|
|
21
27
|
if (frameId !== null) {
|
|
@@ -92,11 +98,13 @@
|
|
|
92
98
|
};
|
|
93
99
|
});
|
|
94
100
|
|
|
95
|
-
|
|
96
|
-
path: undefined
|
|
97
|
-
});
|
|
101
|
+
let path = $derived(value?.path);
|
|
98
102
|
|
|
99
|
-
|
|
103
|
+
$effect(() => {
|
|
104
|
+
if (canvas && mounted && path) {
|
|
105
|
+
reset_scene();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
100
108
|
</script>
|
|
101
109
|
|
|
102
110
|
<canvas bind:this={canvas}></canvas>
|
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
import type { FileData } from "@gradio/client";
|
|
2
|
-
|
|
3
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
-
$$bindings?: Bindings;
|
|
5
|
-
} & Exports;
|
|
6
|
-
(internal: unknown, props: Props & {
|
|
7
|
-
$$events?: Events;
|
|
8
|
-
$$slots?: Slots;
|
|
9
|
-
}): Exports & {
|
|
10
|
-
$set?: any;
|
|
11
|
-
$on?: any;
|
|
12
|
-
};
|
|
13
|
-
z_$$bindings?: Bindings;
|
|
14
|
-
}
|
|
15
|
-
declare const Canvas3DGS: $$__sveltets_2_IsomorphicComponent<{
|
|
2
|
+
type $$ComponentProps = {
|
|
16
3
|
value: FileData;
|
|
17
4
|
zoom_speed: number;
|
|
18
5
|
pan_speed: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
type Canvas3DGS = InstanceType<typeof Canvas3DGS>;
|
|
6
|
+
};
|
|
7
|
+
declare const Canvas3DGS: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type Canvas3DGS = ReturnType<typeof Canvas3DGS>;
|
|
23
9
|
export default Canvas3DGS;
|
|
@@ -7,27 +7,36 @@
|
|
|
7
7
|
import type Canvas3DGS from "./Canvas3DGS.svelte";
|
|
8
8
|
import type Canvas3D from "./Canvas3D.svelte";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
null
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
let {
|
|
11
|
+
value,
|
|
12
|
+
display_mode = "solid",
|
|
13
|
+
clear_color = [0, 0, 0, 0],
|
|
14
|
+
label = "",
|
|
15
|
+
show_label,
|
|
16
|
+
i18n,
|
|
17
|
+
zoom_speed = 1,
|
|
18
|
+
pan_speed = 1,
|
|
19
|
+
camera_position = [null, null, null],
|
|
20
|
+
has_change_history = false
|
|
21
|
+
}: {
|
|
22
|
+
value: FileData | null;
|
|
23
|
+
display_mode?: "solid" | "point_cloud" | "wireframe";
|
|
24
|
+
clear_color?: [number, number, number, number];
|
|
25
|
+
label?: string;
|
|
26
|
+
show_label: boolean;
|
|
27
|
+
i18n: I18nFormatter;
|
|
28
|
+
zoom_speed?: number;
|
|
29
|
+
pan_speed?: number;
|
|
30
|
+
camera_position?: [number | null, number | null, number | null];
|
|
31
|
+
has_change_history?: boolean;
|
|
32
|
+
} = $props();
|
|
25
33
|
|
|
26
|
-
let current_settings = { camera_position, zoom_speed, pan_speed };
|
|
34
|
+
let current_settings = $state({ camera_position, zoom_speed, pan_speed });
|
|
35
|
+
let use_3dgs = $state(false);
|
|
36
|
+
let Canvas3DGSComponent = $state<typeof Canvas3DGS>();
|
|
37
|
+
let Canvas3DComponent = $state<typeof Canvas3D>();
|
|
38
|
+
let canvas3d = $state<Canvas3D | undefined>();
|
|
27
39
|
|
|
28
|
-
let use_3dgs = false;
|
|
29
|
-
let Canvas3DGSComponent: typeof Canvas3DGS;
|
|
30
|
-
let Canvas3DComponent: typeof Canvas3D;
|
|
31
40
|
async function loadCanvas3D(): Promise<typeof Canvas3D> {
|
|
32
41
|
const module = await import("./Canvas3D.svelte");
|
|
33
42
|
return module.default;
|
|
@@ -36,25 +45,27 @@
|
|
|
36
45
|
const module = await import("./Canvas3DGS.svelte");
|
|
37
46
|
return module.default;
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
|
|
49
|
+
$effect(() => {
|
|
50
|
+
if (value) {
|
|
51
|
+
use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
|
|
52
|
+
if (use_3dgs) {
|
|
53
|
+
loadCanvas3DGS().then((component) => {
|
|
54
|
+
Canvas3DGSComponent = component;
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
loadCanvas3D().then((component) => {
|
|
58
|
+
Canvas3DComponent = component;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
49
61
|
}
|
|
50
|
-
}
|
|
62
|
+
});
|
|
51
63
|
|
|
52
|
-
let canvas3d: Canvas3D | undefined;
|
|
53
64
|
function handle_undo(): void {
|
|
54
65
|
canvas3d?.reset_camera_position();
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
$effect(() => {
|
|
58
69
|
if (
|
|
59
70
|
!dequal(current_settings.camera_position, camera_position) ||
|
|
60
71
|
current_settings.zoom_speed !== zoom_speed ||
|
|
@@ -63,7 +74,7 @@
|
|
|
63
74
|
canvas3d?.update_camera(camera_position, zoom_speed, pan_speed);
|
|
64
75
|
current_settings = { camera_position, zoom_speed, pan_speed };
|
|
65
76
|
}
|
|
66
|
-
}
|
|
77
|
+
});
|
|
67
78
|
</script>
|
|
68
79
|
|
|
69
80
|
<BlockLabel
|
|
@@ -79,7 +90,7 @@
|
|
|
79
90
|
<IconButton
|
|
80
91
|
Icon={Undo}
|
|
81
92
|
label="Undo"
|
|
82
|
-
|
|
93
|
+
onclick={() => handle_undo()}
|
|
83
94
|
disabled={!has_change_history}
|
|
84
95
|
/>
|
|
85
96
|
{/if}
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
import type { FileData } from "@gradio/client";
|
|
2
2
|
import type { I18nFormatter } from "@gradio/utils";
|
|
3
|
-
|
|
4
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
5
|
-
$$bindings?: Bindings;
|
|
6
|
-
} & Exports;
|
|
7
|
-
(internal: unknown, props: Props & {
|
|
8
|
-
$$events?: Events;
|
|
9
|
-
$$slots?: Slots;
|
|
10
|
-
}): Exports & {
|
|
11
|
-
$set?: any;
|
|
12
|
-
$on?: any;
|
|
13
|
-
};
|
|
14
|
-
z_$$bindings?: Bindings;
|
|
15
|
-
}
|
|
16
|
-
declare const Model3D: $$__sveltets_2_IsomorphicComponent<{
|
|
3
|
+
type $$ComponentProps = {
|
|
17
4
|
value: FileData | null;
|
|
18
5
|
display_mode?: "solid" | "point_cloud" | "wireframe";
|
|
19
6
|
clear_color?: [number, number, number, number];
|
|
@@ -24,8 +11,7 @@ declare const Model3D: $$__sveltets_2_IsomorphicComponent<{
|
|
|
24
11
|
pan_speed?: number;
|
|
25
12
|
camera_position?: [number | null, number | null, number | null];
|
|
26
13
|
has_change_history?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
type Model3D = InstanceType<typeof Model3D>;
|
|
14
|
+
};
|
|
15
|
+
declare const Model3D: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
16
|
+
type Model3D = ReturnType<typeof Model3D>;
|
|
31
17
|
export default Model3D;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { tick } from "svelte";
|
|
3
3
|
import { Upload, ModifyUpload } from "@gradio/upload";
|
|
4
4
|
import type { FileData, Client } from "@gradio/client";
|
|
5
5
|
import { BlockLabel } from "@gradio/atoms";
|
|
@@ -8,47 +8,56 @@
|
|
|
8
8
|
import type Canvas3DGS from "./Canvas3DGS.svelte";
|
|
9
9
|
import type Canvas3D from "./Canvas3D.svelte";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
let {
|
|
12
|
+
value = $bindable(),
|
|
13
|
+
display_mode = "solid",
|
|
14
|
+
clear_color = [0, 0, 0, 0],
|
|
15
|
+
label = "",
|
|
16
|
+
show_label,
|
|
17
|
+
root,
|
|
18
|
+
i18n,
|
|
19
|
+
zoom_speed = 1,
|
|
20
|
+
pan_speed = 1,
|
|
21
|
+
max_file_size = null,
|
|
22
|
+
uploading = $bindable(),
|
|
23
|
+
upload_promise = $bindable(),
|
|
24
|
+
camera_position = [null, null, null],
|
|
25
|
+
upload,
|
|
26
|
+
stream_handler,
|
|
27
|
+
onchange,
|
|
28
|
+
onclear,
|
|
29
|
+
ondrag,
|
|
30
|
+
onload,
|
|
31
|
+
onerror
|
|
32
|
+
}: {
|
|
33
|
+
value?: FileData | null;
|
|
34
|
+
display_mode?: "solid" | "point_cloud" | "wireframe";
|
|
35
|
+
clear_color?: [number, number, number, number];
|
|
36
|
+
label?: string;
|
|
37
|
+
show_label: boolean;
|
|
38
|
+
root: string;
|
|
39
|
+
i18n: I18nFormatter;
|
|
40
|
+
zoom_speed?: number;
|
|
41
|
+
pan_speed?: number;
|
|
42
|
+
max_file_size?: number | null;
|
|
43
|
+
uploading?: boolean;
|
|
44
|
+
upload_promise?: Promise<(FileData | null)[]> | null;
|
|
45
|
+
camera_position?: [number | null, number | null, number | null];
|
|
46
|
+
upload: Client["upload"];
|
|
47
|
+
stream_handler: Client["stream"];
|
|
48
|
+
onchange?: (value: FileData | null) => void;
|
|
49
|
+
onclear?: () => void;
|
|
50
|
+
ondrag?: (dragging: boolean) => void;
|
|
51
|
+
onload?: (value: FileData) => void;
|
|
52
|
+
onerror?: (error: string) => void;
|
|
53
|
+
} = $props();
|
|
23
54
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
];
|
|
30
|
-
export let upload: Client["upload"];
|
|
31
|
-
export let stream_handler: Client["stream"];
|
|
55
|
+
let use_3dgs = $state(false);
|
|
56
|
+
let Canvas3DGSComponent = $state<typeof Canvas3DGS>();
|
|
57
|
+
let Canvas3DComponent = $state<typeof Canvas3D>();
|
|
58
|
+
let canvas3d = $state<Canvas3D | undefined>();
|
|
59
|
+
let dragging = $state(false);
|
|
32
60
|
|
|
33
|
-
async function handle_upload({
|
|
34
|
-
detail
|
|
35
|
-
}: CustomEvent<FileData>): Promise<void> {
|
|
36
|
-
value = detail;
|
|
37
|
-
await tick();
|
|
38
|
-
dispatch("change", value);
|
|
39
|
-
dispatch("load", value);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function handle_clear(): Promise<void> {
|
|
43
|
-
value = null;
|
|
44
|
-
await tick();
|
|
45
|
-
dispatch("clear");
|
|
46
|
-
dispatch("change");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let use_3dgs = false;
|
|
50
|
-
let Canvas3DGSComponent: typeof Canvas3DGS;
|
|
51
|
-
let Canvas3DComponent: typeof Canvas3D;
|
|
52
61
|
async function loadCanvas3D(): Promise<typeof Canvas3D> {
|
|
53
62
|
const module = await import("./Canvas3D.svelte");
|
|
54
63
|
return module.default;
|
|
@@ -57,34 +66,47 @@
|
|
|
57
66
|
const module = await import("./Canvas3DGS.svelte");
|
|
58
67
|
return module.default;
|
|
59
68
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
$effect(() => {
|
|
71
|
+
if (value) {
|
|
72
|
+
use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
|
|
73
|
+
if (use_3dgs) {
|
|
74
|
+
loadCanvas3DGS().then((component) => {
|
|
75
|
+
Canvas3DGSComponent = component;
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
loadCanvas3D().then((component) => {
|
|
79
|
+
Canvas3DComponent = component;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
70
82
|
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
$effect(() => {
|
|
86
|
+
ondrag?.(dragging);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
async function handle_upload(detail: FileData): Promise<void> {
|
|
90
|
+
value = detail;
|
|
91
|
+
await tick();
|
|
92
|
+
onchange?.(value);
|
|
93
|
+
onload?.(value as FileData);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function handle_clear(): Promise<void> {
|
|
97
|
+
value = null;
|
|
98
|
+
await tick();
|
|
99
|
+
onclear?.();
|
|
100
|
+
onchange?.(null);
|
|
71
101
|
}
|
|
72
102
|
|
|
73
|
-
let canvas3d: Canvas3D | undefined;
|
|
74
103
|
async function handle_undo(): Promise<void> {
|
|
75
104
|
canvas3d?.reset_camera_position();
|
|
76
105
|
}
|
|
77
106
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
drag: boolean;
|
|
82
|
-
load: FileData;
|
|
83
|
-
}>();
|
|
84
|
-
|
|
85
|
-
let dragging = false;
|
|
86
|
-
|
|
87
|
-
$: dispatch("drag", dragging);
|
|
107
|
+
function handle_error(error: string): void {
|
|
108
|
+
onerror?.(error);
|
|
109
|
+
}
|
|
88
110
|
</script>
|
|
89
111
|
|
|
90
112
|
<BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
|
|
@@ -94,13 +116,13 @@
|
|
|
94
116
|
bind:upload_promise
|
|
95
117
|
{upload}
|
|
96
118
|
{stream_handler}
|
|
97
|
-
|
|
119
|
+
onload={handle_upload}
|
|
98
120
|
{root}
|
|
99
121
|
{max_file_size}
|
|
100
122
|
filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj", ".splat", ".ply"]}
|
|
101
123
|
bind:dragging
|
|
102
124
|
bind:uploading
|
|
103
|
-
|
|
125
|
+
onerror={handle_error}
|
|
104
126
|
aria_label={i18n("model3d.drop_to_upload")}
|
|
105
127
|
>
|
|
106
128
|
<slot />
|
|
@@ -109,9 +131,9 @@
|
|
|
109
131
|
<div class="input-model">
|
|
110
132
|
<ModifyUpload
|
|
111
133
|
undoable={!use_3dgs}
|
|
112
|
-
|
|
134
|
+
onclear={handle_clear}
|
|
113
135
|
{i18n}
|
|
114
|
-
|
|
136
|
+
onundo={handle_undo}
|
|
115
137
|
/>
|
|
116
138
|
|
|
117
139
|
{#if use_3dgs}
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import type { FileData, Client } from "@gradio/client";
|
|
2
2
|
import type { I18nFormatter } from "@gradio/utils";
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
value?: FileData | null;
|
|
5
|
+
display_mode?: "solid" | "point_cloud" | "wireframe";
|
|
6
|
+
clear_color?: [number, number, number, number];
|
|
7
|
+
label?: string;
|
|
8
|
+
show_label: boolean;
|
|
9
|
+
root: string;
|
|
10
|
+
i18n: I18nFormatter;
|
|
11
|
+
zoom_speed?: number;
|
|
12
|
+
pan_speed?: number;
|
|
13
|
+
max_file_size?: number | null;
|
|
14
|
+
uploading?: boolean;
|
|
15
|
+
upload_promise?: Promise<(FileData | null)[]> | null;
|
|
16
|
+
camera_position?: [number | null, number | null, number | null];
|
|
17
|
+
upload: Client["upload"];
|
|
18
|
+
stream_handler: Client["stream"];
|
|
19
|
+
onchange?: (value: FileData | null) => void;
|
|
20
|
+
onclear?: () => void;
|
|
21
|
+
ondrag?: (dragging: boolean) => void;
|
|
22
|
+
onload?: (value: FileData) => void;
|
|
23
|
+
onerror?: (error: string) => void;
|
|
24
|
+
};
|
|
3
25
|
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
4
26
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
5
27
|
$$bindings?: Bindings;
|
|
@@ -18,34 +40,12 @@ type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
|
|
|
18
40
|
} ? Props extends Record<string, never> ? any : {
|
|
19
41
|
children?: any;
|
|
20
42
|
} : {});
|
|
21
|
-
declare const Model3DUpload: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren
|
|
22
|
-
value: null | FileData;
|
|
23
|
-
display_mode?: "solid" | "point_cloud" | "wireframe";
|
|
24
|
-
clear_color?: [number, number, number, number];
|
|
25
|
-
label?: string;
|
|
26
|
-
show_label: boolean;
|
|
27
|
-
root: string;
|
|
28
|
-
i18n: I18nFormatter;
|
|
29
|
-
zoom_speed?: number;
|
|
30
|
-
pan_speed?: number;
|
|
31
|
-
max_file_size?: number | null;
|
|
32
|
-
uploading?: boolean;
|
|
33
|
-
upload_promise?: Promise<(FileData | null)[]> | null;
|
|
34
|
-
camera_position?: [number | null, number | null, number | null];
|
|
35
|
-
upload: Client["upload"];
|
|
36
|
-
stream_handler: Client["stream"];
|
|
37
|
-
}, {
|
|
43
|
+
declare const Model3DUpload: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<$$ComponentProps, {
|
|
38
44
|
default: {};
|
|
39
45
|
}>, {
|
|
40
|
-
error: CustomEvent<any>;
|
|
41
|
-
change: CustomEvent<FileData | null>;
|
|
42
|
-
clear: CustomEvent<undefined>;
|
|
43
|
-
drag: CustomEvent<boolean>;
|
|
44
|
-
load: CustomEvent<FileData>;
|
|
45
|
-
} & {
|
|
46
46
|
[evt: string]: CustomEvent<any>;
|
|
47
47
|
}, {
|
|
48
48
|
default: {};
|
|
49
|
-
}, {},
|
|
49
|
+
}, {}, "value" | "uploading" | "upload_promise">;
|
|
50
50
|
type Model3DUpload = InstanceType<typeof Model3DUpload>;
|
|
51
51
|
export default Model3DUpload;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/model3d",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
"@types/babylon": "^6.16.9",
|
|
14
14
|
"dequal": "^2.0.3",
|
|
15
15
|
"gsplat": "^1.2.9",
|
|
16
|
-
"@gradio/atoms": "^0.20.
|
|
17
|
-
"@gradio/
|
|
18
|
-
"@gradio/
|
|
19
|
-
"@gradio/
|
|
20
|
-
"@gradio/
|
|
21
|
-
"@gradio/
|
|
16
|
+
"@gradio/atoms": "^0.20.1",
|
|
17
|
+
"@gradio/client": "^2.0.3",
|
|
18
|
+
"@gradio/upload": "^0.17.4",
|
|
19
|
+
"@gradio/icons": "^0.15.1",
|
|
20
|
+
"@gradio/utils": "^0.11.2",
|
|
21
|
+
"@gradio/statustracker": "^0.12.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@gradio/preview": "^0.15.
|
|
24
|
+
"@gradio/preview": "^0.15.2"
|
|
25
25
|
},
|
|
26
26
|
"main_changeset": true,
|
|
27
27
|
"main": "./Index.svelte",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"./package.json": "./package.json"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"svelte": "^5.
|
|
47
|
+
"svelte": "^5.48.0"
|
|
48
48
|
},
|
|
49
49
|
"repository": {
|
|
50
50
|
"type": "git",
|
package/shared/Canvas3D.svelte
CHANGED
|
@@ -5,19 +5,28 @@
|
|
|
5
5
|
|
|
6
6
|
let BABYLON_VIEWER: typeof import("@babylonjs/viewer");
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
let {
|
|
9
|
+
value,
|
|
10
|
+
display_mode,
|
|
11
|
+
clear_color,
|
|
12
|
+
camera_position,
|
|
13
|
+
zoom_speed,
|
|
14
|
+
pan_speed
|
|
15
|
+
}: {
|
|
16
|
+
value: FileData;
|
|
17
|
+
display_mode: "solid" | "point_cloud" | "wireframe";
|
|
18
|
+
clear_color: [number, number, number, number];
|
|
19
|
+
camera_position: [number | null, number | null, number | null];
|
|
20
|
+
zoom_speed: number;
|
|
21
|
+
pan_speed: number;
|
|
22
|
+
} = $props();
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
let url = $derived(value.url);
|
|
16
25
|
|
|
17
26
|
let canvas: HTMLCanvasElement;
|
|
18
|
-
let viewer
|
|
19
|
-
let viewerDetails
|
|
20
|
-
let mounted = false;
|
|
27
|
+
let viewer = $state<Viewer>();
|
|
28
|
+
let viewerDetails = $state<Readonly<ViewerDetails>>();
|
|
29
|
+
let mounted = $state(false);
|
|
21
30
|
|
|
22
31
|
onMount(() => {
|
|
23
32
|
const initViewer = async (): Promise<void> => {
|
|
@@ -43,9 +52,14 @@
|
|
|
43
52
|
};
|
|
44
53
|
});
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
$effect(() => {
|
|
56
|
+
if (mounted) {
|
|
57
|
+
load_model(url);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
47
60
|
|
|
48
61
|
function setRenderingMode(pointsCloud: boolean, wireframe: boolean): void {
|
|
62
|
+
if (!viewerDetails) return;
|
|
49
63
|
viewerDetails.scene.forcePointsCloud = pointsCloud;
|
|
50
64
|
viewerDetails.scene.forceWireframe = wireframe;
|
|
51
65
|
}
|
|
@@ -81,6 +95,7 @@
|
|
|
81
95
|
zoom_speed: number,
|
|
82
96
|
pan_speed: number
|
|
83
97
|
): void {
|
|
98
|
+
if (!viewerDetails) return;
|
|
84
99
|
const camera = viewerDetails.camera;
|
|
85
100
|
if (camera_position[0] !== null) {
|
|
86
101
|
camera.alpha = (camera_position[0] * Math.PI) / 180;
|
|
@@ -101,7 +116,7 @@
|
|
|
101
116
|
}
|
|
102
117
|
|
|
103
118
|
export function reset_camera_position(): void {
|
|
104
|
-
if (viewerDetails) {
|
|
119
|
+
if (viewerDetails && viewer) {
|
|
105
120
|
viewer.resetCamera();
|
|
106
121
|
}
|
|
107
122
|
}
|
package/shared/Canvas3DGS.svelte
CHANGED
|
@@ -3,19 +3,25 @@
|
|
|
3
3
|
import * as SPLAT from "gsplat";
|
|
4
4
|
import type { FileData } from "@gradio/client";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
let {
|
|
7
|
+
value,
|
|
8
|
+
zoom_speed,
|
|
9
|
+
pan_speed
|
|
10
|
+
}: {
|
|
11
|
+
value: FileData;
|
|
12
|
+
zoom_speed: number;
|
|
13
|
+
pan_speed: number;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
let url = $derived(value.url);
|
|
11
17
|
|
|
12
18
|
let canvas: HTMLCanvasElement;
|
|
13
19
|
let scene: SPLAT.Scene;
|
|
14
20
|
let camera: SPLAT.Camera;
|
|
15
|
-
let renderer
|
|
21
|
+
let renderer = $state<SPLAT.WebGLRenderer | null>(null);
|
|
16
22
|
let controls: SPLAT.OrbitControls;
|
|
17
|
-
let mounted = false;
|
|
18
|
-
let frameId
|
|
23
|
+
let mounted = $state(false);
|
|
24
|
+
let frameId = $state<number | null>(null);
|
|
19
25
|
|
|
20
26
|
function reset_scene(): void {
|
|
21
27
|
if (frameId !== null) {
|
|
@@ -92,11 +98,13 @@
|
|
|
92
98
|
};
|
|
93
99
|
});
|
|
94
100
|
|
|
95
|
-
|
|
96
|
-
path: undefined
|
|
97
|
-
});
|
|
101
|
+
let path = $derived(value?.path);
|
|
98
102
|
|
|
99
|
-
|
|
103
|
+
$effect(() => {
|
|
104
|
+
if (canvas && mounted && path) {
|
|
105
|
+
reset_scene();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
100
108
|
</script>
|
|
101
109
|
|
|
102
110
|
<canvas bind:this={canvas}></canvas>
|
package/shared/Model3D.svelte
CHANGED
|
@@ -7,27 +7,36 @@
|
|
|
7
7
|
import type Canvas3DGS from "./Canvas3DGS.svelte";
|
|
8
8
|
import type Canvas3D from "./Canvas3D.svelte";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
null
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
let {
|
|
11
|
+
value,
|
|
12
|
+
display_mode = "solid",
|
|
13
|
+
clear_color = [0, 0, 0, 0],
|
|
14
|
+
label = "",
|
|
15
|
+
show_label,
|
|
16
|
+
i18n,
|
|
17
|
+
zoom_speed = 1,
|
|
18
|
+
pan_speed = 1,
|
|
19
|
+
camera_position = [null, null, null],
|
|
20
|
+
has_change_history = false
|
|
21
|
+
}: {
|
|
22
|
+
value: FileData | null;
|
|
23
|
+
display_mode?: "solid" | "point_cloud" | "wireframe";
|
|
24
|
+
clear_color?: [number, number, number, number];
|
|
25
|
+
label?: string;
|
|
26
|
+
show_label: boolean;
|
|
27
|
+
i18n: I18nFormatter;
|
|
28
|
+
zoom_speed?: number;
|
|
29
|
+
pan_speed?: number;
|
|
30
|
+
camera_position?: [number | null, number | null, number | null];
|
|
31
|
+
has_change_history?: boolean;
|
|
32
|
+
} = $props();
|
|
25
33
|
|
|
26
|
-
let current_settings = { camera_position, zoom_speed, pan_speed };
|
|
34
|
+
let current_settings = $state({ camera_position, zoom_speed, pan_speed });
|
|
35
|
+
let use_3dgs = $state(false);
|
|
36
|
+
let Canvas3DGSComponent = $state<typeof Canvas3DGS>();
|
|
37
|
+
let Canvas3DComponent = $state<typeof Canvas3D>();
|
|
38
|
+
let canvas3d = $state<Canvas3D | undefined>();
|
|
27
39
|
|
|
28
|
-
let use_3dgs = false;
|
|
29
|
-
let Canvas3DGSComponent: typeof Canvas3DGS;
|
|
30
|
-
let Canvas3DComponent: typeof Canvas3D;
|
|
31
40
|
async function loadCanvas3D(): Promise<typeof Canvas3D> {
|
|
32
41
|
const module = await import("./Canvas3D.svelte");
|
|
33
42
|
return module.default;
|
|
@@ -36,25 +45,27 @@
|
|
|
36
45
|
const module = await import("./Canvas3DGS.svelte");
|
|
37
46
|
return module.default;
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
|
|
49
|
+
$effect(() => {
|
|
50
|
+
if (value) {
|
|
51
|
+
use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
|
|
52
|
+
if (use_3dgs) {
|
|
53
|
+
loadCanvas3DGS().then((component) => {
|
|
54
|
+
Canvas3DGSComponent = component;
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
loadCanvas3D().then((component) => {
|
|
58
|
+
Canvas3DComponent = component;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
49
61
|
}
|
|
50
|
-
}
|
|
62
|
+
});
|
|
51
63
|
|
|
52
|
-
let canvas3d: Canvas3D | undefined;
|
|
53
64
|
function handle_undo(): void {
|
|
54
65
|
canvas3d?.reset_camera_position();
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
$effect(() => {
|
|
58
69
|
if (
|
|
59
70
|
!dequal(current_settings.camera_position, camera_position) ||
|
|
60
71
|
current_settings.zoom_speed !== zoom_speed ||
|
|
@@ -63,7 +74,7 @@
|
|
|
63
74
|
canvas3d?.update_camera(camera_position, zoom_speed, pan_speed);
|
|
64
75
|
current_settings = { camera_position, zoom_speed, pan_speed };
|
|
65
76
|
}
|
|
66
|
-
}
|
|
77
|
+
});
|
|
67
78
|
</script>
|
|
68
79
|
|
|
69
80
|
<BlockLabel
|
|
@@ -79,7 +90,7 @@
|
|
|
79
90
|
<IconButton
|
|
80
91
|
Icon={Undo}
|
|
81
92
|
label="Undo"
|
|
82
|
-
|
|
93
|
+
onclick={() => handle_undo()}
|
|
83
94
|
disabled={!has_change_history}
|
|
84
95
|
/>
|
|
85
96
|
{/if}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { tick } from "svelte";
|
|
3
3
|
import { Upload, ModifyUpload } from "@gradio/upload";
|
|
4
4
|
import type { FileData, Client } from "@gradio/client";
|
|
5
5
|
import { BlockLabel } from "@gradio/atoms";
|
|
@@ -8,47 +8,56 @@
|
|
|
8
8
|
import type Canvas3DGS from "./Canvas3DGS.svelte";
|
|
9
9
|
import type Canvas3D from "./Canvas3D.svelte";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
let {
|
|
12
|
+
value = $bindable(),
|
|
13
|
+
display_mode = "solid",
|
|
14
|
+
clear_color = [0, 0, 0, 0],
|
|
15
|
+
label = "",
|
|
16
|
+
show_label,
|
|
17
|
+
root,
|
|
18
|
+
i18n,
|
|
19
|
+
zoom_speed = 1,
|
|
20
|
+
pan_speed = 1,
|
|
21
|
+
max_file_size = null,
|
|
22
|
+
uploading = $bindable(),
|
|
23
|
+
upload_promise = $bindable(),
|
|
24
|
+
camera_position = [null, null, null],
|
|
25
|
+
upload,
|
|
26
|
+
stream_handler,
|
|
27
|
+
onchange,
|
|
28
|
+
onclear,
|
|
29
|
+
ondrag,
|
|
30
|
+
onload,
|
|
31
|
+
onerror
|
|
32
|
+
}: {
|
|
33
|
+
value?: FileData | null;
|
|
34
|
+
display_mode?: "solid" | "point_cloud" | "wireframe";
|
|
35
|
+
clear_color?: [number, number, number, number];
|
|
36
|
+
label?: string;
|
|
37
|
+
show_label: boolean;
|
|
38
|
+
root: string;
|
|
39
|
+
i18n: I18nFormatter;
|
|
40
|
+
zoom_speed?: number;
|
|
41
|
+
pan_speed?: number;
|
|
42
|
+
max_file_size?: number | null;
|
|
43
|
+
uploading?: boolean;
|
|
44
|
+
upload_promise?: Promise<(FileData | null)[]> | null;
|
|
45
|
+
camera_position?: [number | null, number | null, number | null];
|
|
46
|
+
upload: Client["upload"];
|
|
47
|
+
stream_handler: Client["stream"];
|
|
48
|
+
onchange?: (value: FileData | null) => void;
|
|
49
|
+
onclear?: () => void;
|
|
50
|
+
ondrag?: (dragging: boolean) => void;
|
|
51
|
+
onload?: (value: FileData) => void;
|
|
52
|
+
onerror?: (error: string) => void;
|
|
53
|
+
} = $props();
|
|
23
54
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
];
|
|
30
|
-
export let upload: Client["upload"];
|
|
31
|
-
export let stream_handler: Client["stream"];
|
|
55
|
+
let use_3dgs = $state(false);
|
|
56
|
+
let Canvas3DGSComponent = $state<typeof Canvas3DGS>();
|
|
57
|
+
let Canvas3DComponent = $state<typeof Canvas3D>();
|
|
58
|
+
let canvas3d = $state<Canvas3D | undefined>();
|
|
59
|
+
let dragging = $state(false);
|
|
32
60
|
|
|
33
|
-
async function handle_upload({
|
|
34
|
-
detail
|
|
35
|
-
}: CustomEvent<FileData>): Promise<void> {
|
|
36
|
-
value = detail;
|
|
37
|
-
await tick();
|
|
38
|
-
dispatch("change", value);
|
|
39
|
-
dispatch("load", value);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function handle_clear(): Promise<void> {
|
|
43
|
-
value = null;
|
|
44
|
-
await tick();
|
|
45
|
-
dispatch("clear");
|
|
46
|
-
dispatch("change");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let use_3dgs = false;
|
|
50
|
-
let Canvas3DGSComponent: typeof Canvas3DGS;
|
|
51
|
-
let Canvas3DComponent: typeof Canvas3D;
|
|
52
61
|
async function loadCanvas3D(): Promise<typeof Canvas3D> {
|
|
53
62
|
const module = await import("./Canvas3D.svelte");
|
|
54
63
|
return module.default;
|
|
@@ -57,34 +66,47 @@
|
|
|
57
66
|
const module = await import("./Canvas3DGS.svelte");
|
|
58
67
|
return module.default;
|
|
59
68
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
$effect(() => {
|
|
71
|
+
if (value) {
|
|
72
|
+
use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
|
|
73
|
+
if (use_3dgs) {
|
|
74
|
+
loadCanvas3DGS().then((component) => {
|
|
75
|
+
Canvas3DGSComponent = component;
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
loadCanvas3D().then((component) => {
|
|
79
|
+
Canvas3DComponent = component;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
70
82
|
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
$effect(() => {
|
|
86
|
+
ondrag?.(dragging);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
async function handle_upload(detail: FileData): Promise<void> {
|
|
90
|
+
value = detail;
|
|
91
|
+
await tick();
|
|
92
|
+
onchange?.(value);
|
|
93
|
+
onload?.(value as FileData);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function handle_clear(): Promise<void> {
|
|
97
|
+
value = null;
|
|
98
|
+
await tick();
|
|
99
|
+
onclear?.();
|
|
100
|
+
onchange?.(null);
|
|
71
101
|
}
|
|
72
102
|
|
|
73
|
-
let canvas3d: Canvas3D | undefined;
|
|
74
103
|
async function handle_undo(): Promise<void> {
|
|
75
104
|
canvas3d?.reset_camera_position();
|
|
76
105
|
}
|
|
77
106
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
drag: boolean;
|
|
82
|
-
load: FileData;
|
|
83
|
-
}>();
|
|
84
|
-
|
|
85
|
-
let dragging = false;
|
|
86
|
-
|
|
87
|
-
$: dispatch("drag", dragging);
|
|
107
|
+
function handle_error(error: string): void {
|
|
108
|
+
onerror?.(error);
|
|
109
|
+
}
|
|
88
110
|
</script>
|
|
89
111
|
|
|
90
112
|
<BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
|
|
@@ -94,13 +116,13 @@
|
|
|
94
116
|
bind:upload_promise
|
|
95
117
|
{upload}
|
|
96
118
|
{stream_handler}
|
|
97
|
-
|
|
119
|
+
onload={handle_upload}
|
|
98
120
|
{root}
|
|
99
121
|
{max_file_size}
|
|
100
122
|
filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj", ".splat", ".ply"]}
|
|
101
123
|
bind:dragging
|
|
102
124
|
bind:uploading
|
|
103
|
-
|
|
125
|
+
onerror={handle_error}
|
|
104
126
|
aria_label={i18n("model3d.drop_to_upload")}
|
|
105
127
|
>
|
|
106
128
|
<slot />
|
|
@@ -109,9 +131,9 @@
|
|
|
109
131
|
<div class="input-model">
|
|
110
132
|
<ModifyUpload
|
|
111
133
|
undoable={!use_3dgs}
|
|
112
|
-
|
|
134
|
+
onclear={handle_clear}
|
|
113
135
|
{i18n}
|
|
114
|
-
|
|
136
|
+
onundo={handle_undo}
|
|
115
137
|
/>
|
|
116
138
|
|
|
117
139
|
{#if use_3dgs}
|