@gradio/model3d 0.15.1 → 0.16.1
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 +32 -0
- package/Index.svelte +21 -7
- package/dist/Index.svelte +21 -7
- 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 +85 -61
- package/dist/shared/Model3DUpload.svelte.d.ts +24 -24
- package/dist/types.d.ts +5 -0
- package/package.json +7 -7
- package/shared/Canvas3D.svelte +27 -12
- package/shared/Canvas3DGS.svelte +20 -12
- package/shared/Model3D.svelte +45 -34
- package/shared/Model3DUpload.svelte +85 -61
- package/types.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
# @gradio/model3d
|
|
2
|
+
|
|
3
|
+
## 0.16.1
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- [#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!
|
|
8
|
+
|
|
9
|
+
### Dependency updates
|
|
10
|
+
|
|
11
|
+
- @gradio/utils@0.11.1
|
|
12
|
+
- @gradio/client@2.0.2
|
|
13
|
+
|
|
14
|
+
## 0.16.0
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
- [#12539](https://github.com/gradio-app/gradio/pull/12539) [`f1d83fa`](https://github.com/gradio-app/gradio/commit/f1d83fac3d6e4bad60cf896a026fa2d572f26073) - Add ability to add custom buttons to components. Thanks @abidlabs!
|
|
19
|
+
|
|
20
|
+
### Dependency updates
|
|
21
|
+
|
|
22
|
+
- @gradio/atoms@0.20.0
|
|
23
|
+
- @gradio/utils@0.11.0
|
|
24
|
+
- @gradio/client@2.0.1
|
|
25
|
+
- @gradio/statustracker@0.12.1
|
|
26
|
+
- @gradio/upload@0.17.3
|
|
27
|
+
|
|
28
|
+
## 0.15.1
|
|
29
|
+
|
|
30
|
+
### Dependency updates
|
|
31
|
+
|
|
32
|
+
- @gradio/utils@0.10.4
|
|
33
|
+
|
|
2
34
|
## 0.15.1
|
|
3
35
|
|
|
4
36
|
### Features
|
package/Index.svelte
CHANGED
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
import { Gradio } from "@gradio/utils";
|
|
12
12
|
import Model3D from "./shared/Model3D.svelte";
|
|
13
13
|
import Model3DUpload from "./shared/Model3DUpload.svelte";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
BlockLabel,
|
|
16
|
+
Block,
|
|
17
|
+
Empty,
|
|
18
|
+
UploadText,
|
|
19
|
+
IconButtonWrapper
|
|
20
|
+
} from "@gradio/atoms";
|
|
15
21
|
import { File } from "@gradio/icons";
|
|
16
22
|
import { StatusTracker } from "@gradio/statustracker";
|
|
17
23
|
|
|
@@ -106,6 +112,14 @@
|
|
|
106
112
|
{has_change_history}
|
|
107
113
|
/>
|
|
108
114
|
{:else}
|
|
115
|
+
{#if gradio.shared.show_label && gradio.props.buttons && gradio.props.buttons.length > 0}
|
|
116
|
+
<IconButtonWrapper
|
|
117
|
+
buttons={gradio.props.buttons}
|
|
118
|
+
on_custom_button_click={(id) => {
|
|
119
|
+
gradio.dispatch("custom_button_click", { id });
|
|
120
|
+
}}
|
|
121
|
+
/>
|
|
122
|
+
{/if}
|
|
109
123
|
<BlockLabel
|
|
110
124
|
show_label={gradio.shared.show_label}
|
|
111
125
|
Icon={File}
|
|
@@ -143,15 +157,15 @@
|
|
|
143
157
|
root={gradio.shared.root}
|
|
144
158
|
display_mode={gradio.props.display_mode}
|
|
145
159
|
clear_color={gradio.props.clear_color}
|
|
146
|
-
value={gradio.props.value}
|
|
160
|
+
bind:value={gradio.props.value}
|
|
147
161
|
camera_position={gradio.props.camera_position}
|
|
148
162
|
zoom_speed={gradio.props.zoom_speed}
|
|
149
163
|
bind:uploading
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
onchange={handle_change}
|
|
165
|
+
ondrag={handle_drag}
|
|
166
|
+
onclear={handle_clear}
|
|
167
|
+
onload={handle_load}
|
|
168
|
+
onerror={handle_error}
|
|
155
169
|
i18n={gradio.i18n}
|
|
156
170
|
max_file_size={gradio.shared.max_file_size}
|
|
157
171
|
upload={(...args) => gradio.shared.client.upload(...args)}
|
package/dist/Index.svelte
CHANGED
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
import { Gradio } from "@gradio/utils";
|
|
12
12
|
import Model3D from "./shared/Model3D.svelte";
|
|
13
13
|
import Model3DUpload from "./shared/Model3DUpload.svelte";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
BlockLabel,
|
|
16
|
+
Block,
|
|
17
|
+
Empty,
|
|
18
|
+
UploadText,
|
|
19
|
+
IconButtonWrapper
|
|
20
|
+
} from "@gradio/atoms";
|
|
15
21
|
import { File } from "@gradio/icons";
|
|
16
22
|
import { StatusTracker } from "@gradio/statustracker";
|
|
17
23
|
|
|
@@ -106,6 +112,14 @@
|
|
|
106
112
|
{has_change_history}
|
|
107
113
|
/>
|
|
108
114
|
{:else}
|
|
115
|
+
{#if gradio.shared.show_label && gradio.props.buttons && gradio.props.buttons.length > 0}
|
|
116
|
+
<IconButtonWrapper
|
|
117
|
+
buttons={gradio.props.buttons}
|
|
118
|
+
on_custom_button_click={(id) => {
|
|
119
|
+
gradio.dispatch("custom_button_click", { id });
|
|
120
|
+
}}
|
|
121
|
+
/>
|
|
122
|
+
{/if}
|
|
109
123
|
<BlockLabel
|
|
110
124
|
show_label={gradio.shared.show_label}
|
|
111
125
|
Icon={File}
|
|
@@ -143,15 +157,15 @@
|
|
|
143
157
|
root={gradio.shared.root}
|
|
144
158
|
display_mode={gradio.props.display_mode}
|
|
145
159
|
clear_color={gradio.props.clear_color}
|
|
146
|
-
value={gradio.props.value}
|
|
160
|
+
bind:value={gradio.props.value}
|
|
147
161
|
camera_position={gradio.props.camera_position}
|
|
148
162
|
zoom_speed={gradio.props.zoom_speed}
|
|
149
163
|
bind:uploading
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
onchange={handle_change}
|
|
165
|
+
ondrag={handle_drag}
|
|
166
|
+
onclear={handle_clear}
|
|
167
|
+
onload={handle_load}
|
|
168
|
+
onerror={handle_error}
|
|
155
169
|
i18n={gradio.i18n}
|
|
156
170
|
max_file_size={gradio.shared.max_file_size}
|
|
157
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,83 +8,107 @@
|
|
|
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
|
-
|
|
31
|
-
|
|
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);
|
|
60
|
+
|
|
61
|
+
async function loadCanvas3D(): Promise<typeof Canvas3D> {
|
|
62
|
+
const module = await import("./Canvas3D.svelte");
|
|
63
|
+
return module.default;
|
|
64
|
+
}
|
|
65
|
+
async function loadCanvas3DGS(): Promise<typeof Canvas3DGS> {
|
|
66
|
+
const module = await import("./Canvas3DGS.svelte");
|
|
67
|
+
return module.default;
|
|
68
|
+
}
|
|
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
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
$effect(() => {
|
|
86
|
+
ondrag?.(dragging);
|
|
87
|
+
});
|
|
32
88
|
|
|
33
89
|
async function handle_upload({
|
|
34
90
|
detail
|
|
35
91
|
}: CustomEvent<FileData>): Promise<void> {
|
|
36
92
|
value = detail;
|
|
37
93
|
await tick();
|
|
38
|
-
|
|
39
|
-
|
|
94
|
+
onchange?.(value);
|
|
95
|
+
onload?.(value as FileData);
|
|
40
96
|
}
|
|
41
97
|
|
|
42
98
|
async function handle_clear(): Promise<void> {
|
|
43
99
|
value = null;
|
|
44
100
|
await tick();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let use_3dgs = false;
|
|
50
|
-
let Canvas3DGSComponent: typeof Canvas3DGS;
|
|
51
|
-
let Canvas3DComponent: typeof Canvas3D;
|
|
52
|
-
async function loadCanvas3D(): Promise<typeof Canvas3D> {
|
|
53
|
-
const module = await import("./Canvas3D.svelte");
|
|
54
|
-
return module.default;
|
|
55
|
-
}
|
|
56
|
-
async function loadCanvas3DGS(): Promise<typeof Canvas3DGS> {
|
|
57
|
-
const module = await import("./Canvas3DGS.svelte");
|
|
58
|
-
return module.default;
|
|
59
|
-
}
|
|
60
|
-
$: if (value) {
|
|
61
|
-
use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
|
|
62
|
-
if (use_3dgs) {
|
|
63
|
-
loadCanvas3DGS().then((component) => {
|
|
64
|
-
Canvas3DGSComponent = component;
|
|
65
|
-
});
|
|
66
|
-
} else {
|
|
67
|
-
loadCanvas3D().then((component) => {
|
|
68
|
-
Canvas3DComponent = component;
|
|
69
|
-
});
|
|
70
|
-
}
|
|
101
|
+
onclear?.();
|
|
102
|
+
onchange?.(null);
|
|
71
103
|
}
|
|
72
104
|
|
|
73
|
-
let canvas3d: Canvas3D | undefined;
|
|
74
105
|
async function handle_undo(): Promise<void> {
|
|
75
106
|
canvas3d?.reset_camera_position();
|
|
76
107
|
}
|
|
77
108
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
drag: boolean;
|
|
82
|
-
load: FileData;
|
|
83
|
-
}>();
|
|
84
|
-
|
|
85
|
-
let dragging = false;
|
|
86
|
-
|
|
87
|
-
$: dispatch("drag", dragging);
|
|
109
|
+
function handle_error({ detail }: CustomEvent<string>): void {
|
|
110
|
+
onerror?.(detail);
|
|
111
|
+
}
|
|
88
112
|
</script>
|
|
89
113
|
|
|
90
114
|
<BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
|
|
@@ -100,7 +124,7 @@
|
|
|
100
124
|
filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj", ".splat", ".ply"]}
|
|
101
125
|
bind:dragging
|
|
102
126
|
bind:uploading
|
|
103
|
-
on:error
|
|
127
|
+
on:error={handle_error}
|
|
104
128
|
aria_label={i18n("model3d.drop_to_upload")}
|
|
105
129
|
>
|
|
106
130
|
<slot />
|
|
@@ -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/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FileData } from "@gradio/client";
|
|
2
2
|
import type { LoadingStatus } from "js/statustracker";
|
|
3
|
+
import type { CustomButton } from "@gradio/utils";
|
|
3
4
|
export interface Model3DProps {
|
|
4
5
|
value: null | FileData;
|
|
5
6
|
display_mode: "solid" | "point_cloud" | "wireframe";
|
|
@@ -8,6 +9,7 @@ export interface Model3DProps {
|
|
|
8
9
|
zoom_speed: number;
|
|
9
10
|
has_change_history: boolean;
|
|
10
11
|
camera_position: [number | null, number | null, number | null];
|
|
12
|
+
buttons: (string | CustomButton)[] | null;
|
|
11
13
|
}
|
|
12
14
|
export interface Model3DEvents {
|
|
13
15
|
change: FileData | null;
|
|
@@ -16,4 +18,7 @@ export interface Model3DEvents {
|
|
|
16
18
|
clear: never;
|
|
17
19
|
clear_status: LoadingStatus;
|
|
18
20
|
error: string;
|
|
21
|
+
custom_button_click: {
|
|
22
|
+
id: number;
|
|
23
|
+
};
|
|
19
24
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/model3d",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
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.
|
|
17
|
-
"@gradio/client": "^2.0.
|
|
18
|
-
"@gradio/statustracker": "^0.12.
|
|
16
|
+
"@gradio/atoms": "^0.20.0",
|
|
17
|
+
"@gradio/client": "^2.0.2",
|
|
18
|
+
"@gradio/statustracker": "^0.12.1",
|
|
19
19
|
"@gradio/icons": "^0.15.0",
|
|
20
|
-
"@gradio/upload": "^0.17.
|
|
21
|
-
"@gradio/utils": "^0.
|
|
20
|
+
"@gradio/upload": "^0.17.3",
|
|
21
|
+
"@gradio/utils": "^0.11.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@gradio/preview": "^0.15.
|
|
24
|
+
"@gradio/preview": "^0.15.1"
|
|
25
25
|
},
|
|
26
26
|
"main_changeset": true,
|
|
27
27
|
"main": "./Index.svelte",
|
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,83 +8,107 @@
|
|
|
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
|
-
|
|
31
|
-
|
|
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);
|
|
60
|
+
|
|
61
|
+
async function loadCanvas3D(): Promise<typeof Canvas3D> {
|
|
62
|
+
const module = await import("./Canvas3D.svelte");
|
|
63
|
+
return module.default;
|
|
64
|
+
}
|
|
65
|
+
async function loadCanvas3DGS(): Promise<typeof Canvas3DGS> {
|
|
66
|
+
const module = await import("./Canvas3DGS.svelte");
|
|
67
|
+
return module.default;
|
|
68
|
+
}
|
|
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
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
$effect(() => {
|
|
86
|
+
ondrag?.(dragging);
|
|
87
|
+
});
|
|
32
88
|
|
|
33
89
|
async function handle_upload({
|
|
34
90
|
detail
|
|
35
91
|
}: CustomEvent<FileData>): Promise<void> {
|
|
36
92
|
value = detail;
|
|
37
93
|
await tick();
|
|
38
|
-
|
|
39
|
-
|
|
94
|
+
onchange?.(value);
|
|
95
|
+
onload?.(value as FileData);
|
|
40
96
|
}
|
|
41
97
|
|
|
42
98
|
async function handle_clear(): Promise<void> {
|
|
43
99
|
value = null;
|
|
44
100
|
await tick();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let use_3dgs = false;
|
|
50
|
-
let Canvas3DGSComponent: typeof Canvas3DGS;
|
|
51
|
-
let Canvas3DComponent: typeof Canvas3D;
|
|
52
|
-
async function loadCanvas3D(): Promise<typeof Canvas3D> {
|
|
53
|
-
const module = await import("./Canvas3D.svelte");
|
|
54
|
-
return module.default;
|
|
55
|
-
}
|
|
56
|
-
async function loadCanvas3DGS(): Promise<typeof Canvas3DGS> {
|
|
57
|
-
const module = await import("./Canvas3DGS.svelte");
|
|
58
|
-
return module.default;
|
|
59
|
-
}
|
|
60
|
-
$: if (value) {
|
|
61
|
-
use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
|
|
62
|
-
if (use_3dgs) {
|
|
63
|
-
loadCanvas3DGS().then((component) => {
|
|
64
|
-
Canvas3DGSComponent = component;
|
|
65
|
-
});
|
|
66
|
-
} else {
|
|
67
|
-
loadCanvas3D().then((component) => {
|
|
68
|
-
Canvas3DComponent = component;
|
|
69
|
-
});
|
|
70
|
-
}
|
|
101
|
+
onclear?.();
|
|
102
|
+
onchange?.(null);
|
|
71
103
|
}
|
|
72
104
|
|
|
73
|
-
let canvas3d: Canvas3D | undefined;
|
|
74
105
|
async function handle_undo(): Promise<void> {
|
|
75
106
|
canvas3d?.reset_camera_position();
|
|
76
107
|
}
|
|
77
108
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
drag: boolean;
|
|
82
|
-
load: FileData;
|
|
83
|
-
}>();
|
|
84
|
-
|
|
85
|
-
let dragging = false;
|
|
86
|
-
|
|
87
|
-
$: dispatch("drag", dragging);
|
|
109
|
+
function handle_error({ detail }: CustomEvent<string>): void {
|
|
110
|
+
onerror?.(detail);
|
|
111
|
+
}
|
|
88
112
|
</script>
|
|
89
113
|
|
|
90
114
|
<BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
|
|
@@ -100,7 +124,7 @@
|
|
|
100
124
|
filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj", ".splat", ".ply"]}
|
|
101
125
|
bind:dragging
|
|
102
126
|
bind:uploading
|
|
103
|
-
on:error
|
|
127
|
+
on:error={handle_error}
|
|
104
128
|
aria_label={i18n("model3d.drop_to_upload")}
|
|
105
129
|
>
|
|
106
130
|
<slot />
|
package/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FileData } from "@gradio/client";
|
|
2
2
|
import type { LoadingStatus } from "js/statustracker";
|
|
3
|
+
import type { CustomButton } from "@gradio/utils";
|
|
3
4
|
|
|
4
5
|
export interface Model3DProps {
|
|
5
6
|
value: null | FileData;
|
|
@@ -9,6 +10,7 @@ export interface Model3DProps {
|
|
|
9
10
|
zoom_speed: number;
|
|
10
11
|
has_change_history: boolean;
|
|
11
12
|
camera_position: [number | null, number | null, number | null];
|
|
13
|
+
buttons: (string | CustomButton)[] | null;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export interface Model3DEvents {
|
|
@@ -18,4 +20,5 @@ export interface Model3DEvents {
|
|
|
18
20
|
clear: never;
|
|
19
21
|
clear_status: LoadingStatus;
|
|
20
22
|
error: string;
|
|
23
|
+
custom_button_click: { id: number };
|
|
21
24
|
}
|