@gradio/model3d 0.12.0 → 0.12.2-beta.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 +48 -0
- package/Index.svelte +2 -1
- package/dist/Example.svelte +18 -0
- package/dist/Example.svelte.d.ts +18 -0
- package/dist/Index.svelte +130 -0
- package/dist/Index.svelte.d.ts +38 -0
- package/dist/shared/Canvas3D.svelte +177 -0
- package/dist/shared/Canvas3D.svelte.d.ts +25 -0
- package/dist/shared/Canvas3DGS.svelte +103 -0
- package/dist/shared/Canvas3DGS.svelte.d.ts +20 -0
- package/dist/shared/Model3D.svelte +124 -0
- package/dist/shared/Model3D.svelte.d.ts +26 -0
- package/dist/shared/Model3DUpload.svelte +135 -0
- package/dist/shared/Model3DUpload.svelte.d.ts +38 -0
- package/package.json +27 -11
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,53 @@
|
|
1
1
|
# @gradio/model3d
|
2
2
|
|
3
|
+
## 0.12.2-beta.1
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
- [#9187](https://github.com/gradio-app/gradio/pull/9187) [`5bf00b7`](https://github.com/gradio-app/gradio/commit/5bf00b7524ebf399b48719120a49d15bb21bd65c) - make all component SSR compatible. Thanks @pngwn!
|
8
|
+
|
9
|
+
### Dependency updates
|
10
|
+
|
11
|
+
- @gradio/atoms@0.8.1-beta.1
|
12
|
+
- @gradio/icons@0.8.0-beta.1
|
13
|
+
- @gradio/statustracker@0.8.0-beta.1
|
14
|
+
- @gradio/utils@0.7.0-beta.1
|
15
|
+
- @gradio/client@1.6.0-beta.1
|
16
|
+
- @gradio/upload@0.12.4-beta.1
|
17
|
+
- @gradio/wasm@0.13.1-beta.1
|
18
|
+
|
19
|
+
## 0.12.2-beta.0
|
20
|
+
|
21
|
+
### Fixes
|
22
|
+
|
23
|
+
- [#9163](https://github.com/gradio-app/gradio/pull/9163) [`2b6cbf2`](https://github.com/gradio-app/gradio/commit/2b6cbf25908e42cf027324e54ef2cc0baad11a91) - fix exports and generate types. Thanks @pngwn!
|
24
|
+
|
25
|
+
### Dependency updates
|
26
|
+
|
27
|
+
- @gradio/utils@0.7.0-beta.0
|
28
|
+
- @gradio/statustracker@0.8.0-beta.0
|
29
|
+
- @gradio/atoms@0.8.1-beta.0
|
30
|
+
- @gradio/client@1.6.0-beta.0
|
31
|
+
- @gradio/icons@0.8.0-beta.0
|
32
|
+
- @gradio/upload@0.12.4-beta.0
|
33
|
+
- @gradio/wasm@0.13.1-beta.0
|
34
|
+
|
35
|
+
## 0.12.1
|
36
|
+
|
37
|
+
### Features
|
38
|
+
|
39
|
+
- [#9118](https://github.com/gradio-app/gradio/pull/9118) [`e1c404d`](https://github.com/gradio-app/gradio/commit/e1c404da1143fb52b659d03e028bdba1badf443d) - setup npm-previews of all packages. Thanks @pngwn!
|
40
|
+
|
41
|
+
### Dependency updates
|
42
|
+
|
43
|
+
- @gradio/utils@0.6.0
|
44
|
+
- @gradio/upload@0.12.3
|
45
|
+
- @gradio/atoms@0.8.0
|
46
|
+
- @gradio/client@1.5.1
|
47
|
+
- @gradio/statustracker@0.7.5
|
48
|
+
- @gradio/wasm@0.13.0
|
49
|
+
- @gradio/icons@0.7.1
|
50
|
+
|
3
51
|
## 0.12.0
|
4
52
|
|
5
53
|
### Features
|
package/Index.svelte
CHANGED
@@ -41,6 +41,7 @@
|
|
41
41
|
export let interactive: boolean;
|
42
42
|
|
43
43
|
let dragging = false;
|
44
|
+
const is_browser = typeof window !== "undefined";
|
44
45
|
</script>
|
45
46
|
|
46
47
|
{#if !interactive}
|
@@ -63,7 +64,7 @@
|
|
63
64
|
on:clear_status={() => gradio.dispatch("clear_status", loading_status)}
|
64
65
|
/>
|
65
66
|
|
66
|
-
{#if value}
|
67
|
+
{#if value && is_browser}
|
67
68
|
<Model3D
|
68
69
|
{value}
|
69
70
|
i18n={gradio.i18n}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<script>export let value;
|
2
|
+
export let type;
|
3
|
+
export let selected = false;
|
4
|
+
</script>
|
5
|
+
|
6
|
+
<div
|
7
|
+
class:table={type === "table"}
|
8
|
+
class:gallery={type === "gallery"}
|
9
|
+
class:selected
|
10
|
+
>
|
11
|
+
{value ? value : ""}
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<style>
|
15
|
+
.gallery {
|
16
|
+
padding: var(--size-1) var(--size-2);
|
17
|
+
}
|
18
|
+
</style>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
declare const __propDef: {
|
3
|
+
props: {
|
4
|
+
value: string | null;
|
5
|
+
type: "gallery" | "table";
|
6
|
+
selected?: boolean | undefined;
|
7
|
+
};
|
8
|
+
events: {
|
9
|
+
[evt: string]: CustomEvent<any>;
|
10
|
+
};
|
11
|
+
slots: {};
|
12
|
+
};
|
13
|
+
export type ExampleProps = typeof __propDef.props;
|
14
|
+
export type ExampleEvents = typeof __propDef.events;
|
15
|
+
export type ExampleSlots = typeof __propDef.slots;
|
16
|
+
export default class Example extends SvelteComponent<ExampleProps, ExampleEvents, ExampleSlots> {
|
17
|
+
}
|
18
|
+
export {};
|
@@ -0,0 +1,130 @@
|
|
1
|
+
<script context="module">export { default as BaseModel3D } from "./shared/Model3D.svelte";
|
2
|
+
export { default as BaseModel3DUpload } from "./shared/Model3DUpload.svelte";
|
3
|
+
export { default as BaseExample } from "./Example.svelte";
|
4
|
+
</script>
|
5
|
+
|
6
|
+
<script>import Model3D from "./shared/Model3D.svelte";
|
7
|
+
import Model3DUpload from "./shared/Model3DUpload.svelte";
|
8
|
+
import { BlockLabel, Block, Empty, UploadText } from "@gradio/atoms";
|
9
|
+
import { File } from "@gradio/icons";
|
10
|
+
import { StatusTracker } from "@gradio/statustracker";
|
11
|
+
export let elem_id = "";
|
12
|
+
export let elem_classes = [];
|
13
|
+
export let visible = true;
|
14
|
+
export let value = null;
|
15
|
+
export let root;
|
16
|
+
export let display_mode = "solid";
|
17
|
+
export let clear_color;
|
18
|
+
export let loading_status;
|
19
|
+
export let label;
|
20
|
+
export let show_label;
|
21
|
+
export let container = true;
|
22
|
+
export let scale = null;
|
23
|
+
export let min_width = void 0;
|
24
|
+
export let gradio;
|
25
|
+
export let height = void 0;
|
26
|
+
export let zoom_speed = 1;
|
27
|
+
export let camera_position = [
|
28
|
+
null,
|
29
|
+
null,
|
30
|
+
null
|
31
|
+
];
|
32
|
+
export let interactive;
|
33
|
+
let dragging = false;
|
34
|
+
const is_browser = typeof window !== "undefined";
|
35
|
+
</script>
|
36
|
+
|
37
|
+
{#if !interactive}
|
38
|
+
<Block
|
39
|
+
{visible}
|
40
|
+
variant={value === null ? "dashed" : "solid"}
|
41
|
+
border_mode={dragging ? "focus" : "base"}
|
42
|
+
padding={false}
|
43
|
+
{elem_id}
|
44
|
+
{elem_classes}
|
45
|
+
{container}
|
46
|
+
{scale}
|
47
|
+
{min_width}
|
48
|
+
{height}
|
49
|
+
>
|
50
|
+
<StatusTracker
|
51
|
+
autoscroll={gradio.autoscroll}
|
52
|
+
i18n={gradio.i18n}
|
53
|
+
{...loading_status}
|
54
|
+
on:clear_status={() => gradio.dispatch("clear_status", loading_status)}
|
55
|
+
/>
|
56
|
+
|
57
|
+
{#if value && is_browser}
|
58
|
+
<Model3D
|
59
|
+
{value}
|
60
|
+
i18n={gradio.i18n}
|
61
|
+
{display_mode}
|
62
|
+
{clear_color}
|
63
|
+
{label}
|
64
|
+
{show_label}
|
65
|
+
{camera_position}
|
66
|
+
{zoom_speed}
|
67
|
+
/>
|
68
|
+
{:else}
|
69
|
+
<!-- Not ideal but some bugs to work out before we can
|
70
|
+
make this consistent with other components -->
|
71
|
+
|
72
|
+
<BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
|
73
|
+
|
74
|
+
<Empty unpadded_box={true} size="large"><File /></Empty>
|
75
|
+
{/if}
|
76
|
+
</Block>
|
77
|
+
{:else}
|
78
|
+
<Block
|
79
|
+
{visible}
|
80
|
+
variant={value === null ? "dashed" : "solid"}
|
81
|
+
border_mode={dragging ? "focus" : "base"}
|
82
|
+
padding={false}
|
83
|
+
{elem_id}
|
84
|
+
{elem_classes}
|
85
|
+
{container}
|
86
|
+
{scale}
|
87
|
+
{min_width}
|
88
|
+
{height}
|
89
|
+
>
|
90
|
+
<StatusTracker
|
91
|
+
autoscroll={gradio.autoscroll}
|
92
|
+
i18n={gradio.i18n}
|
93
|
+
{...loading_status}
|
94
|
+
on:clear_status={() => gradio.dispatch("clear_status", loading_status)}
|
95
|
+
/>
|
96
|
+
|
97
|
+
<Model3DUpload
|
98
|
+
{label}
|
99
|
+
{show_label}
|
100
|
+
{root}
|
101
|
+
{display_mode}
|
102
|
+
{clear_color}
|
103
|
+
{value}
|
104
|
+
{camera_position}
|
105
|
+
{zoom_speed}
|
106
|
+
on:change={({ detail }) => (value = detail)}
|
107
|
+
on:drag={({ detail }) => (dragging = detail)}
|
108
|
+
on:change={({ detail }) => gradio.dispatch("change", detail)}
|
109
|
+
on:clear={() => {
|
110
|
+
value = null;
|
111
|
+
gradio.dispatch("clear");
|
112
|
+
}}
|
113
|
+
on:load={({ detail }) => {
|
114
|
+
value = detail;
|
115
|
+
gradio.dispatch("upload");
|
116
|
+
}}
|
117
|
+
on:error={({ detail }) => {
|
118
|
+
loading_status = loading_status || {};
|
119
|
+
loading_status.status = "error";
|
120
|
+
gradio.dispatch("error", detail);
|
121
|
+
}}
|
122
|
+
i18n={gradio.i18n}
|
123
|
+
max_file_size={gradio.max_file_size}
|
124
|
+
upload={gradio.client.upload}
|
125
|
+
stream_handler={gradio.client.stream}
|
126
|
+
>
|
127
|
+
<UploadText i18n={gradio.i18n} type="file" />
|
128
|
+
</Model3DUpload>
|
129
|
+
</Block>
|
130
|
+
{/if}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
export { default as BaseModel3D } from "./shared/Model3D.svelte";
|
3
|
+
export { default as BaseModel3DUpload } from "./shared/Model3DUpload.svelte";
|
4
|
+
export { default as BaseExample } from "./Example.svelte";
|
5
|
+
import type { FileData } from "@gradio/client";
|
6
|
+
import type { LoadingStatus } from "@gradio/statustracker";
|
7
|
+
import type { Gradio } from "@gradio/utils";
|
8
|
+
declare const __propDef: {
|
9
|
+
props: {
|
10
|
+
elem_id?: string | undefined;
|
11
|
+
elem_classes?: string[] | undefined;
|
12
|
+
visible?: boolean | undefined;
|
13
|
+
value?: (null | FileData) | undefined;
|
14
|
+
root: string;
|
15
|
+
display_mode?: ("solid" | "point_cloud" | "wireframe") | undefined;
|
16
|
+
clear_color: [number, number, number, number];
|
17
|
+
loading_status: LoadingStatus;
|
18
|
+
label: string;
|
19
|
+
show_label: boolean;
|
20
|
+
container?: boolean | undefined;
|
21
|
+
scale?: (number | null) | undefined;
|
22
|
+
min_width?: number | undefined;
|
23
|
+
gradio: Gradio;
|
24
|
+
height?: number | undefined;
|
25
|
+
zoom_speed?: number | undefined;
|
26
|
+
camera_position?: [number | null, number | null, number | null] | undefined;
|
27
|
+
interactive: boolean;
|
28
|
+
};
|
29
|
+
events: {
|
30
|
+
[evt: string]: CustomEvent<any>;
|
31
|
+
};
|
32
|
+
slots: {};
|
33
|
+
};
|
34
|
+
export type IndexProps = typeof __propDef.props;
|
35
|
+
export type IndexEvents = typeof __propDef.events;
|
36
|
+
export type IndexSlots = typeof __propDef.slots;
|
37
|
+
export default class Index extends SvelteComponent<IndexProps, IndexEvents, IndexSlots> {
|
38
|
+
}
|
@@ -0,0 +1,177 @@
|
|
1
|
+
<script>import { onMount } from "svelte";
|
2
|
+
import * as BABYLON from "babylonjs";
|
3
|
+
import * as BABYLON_LOADERS from "babylonjs-loaders";
|
4
|
+
import { resolve_wasm_src } from "@gradio/wasm/svelte";
|
5
|
+
$: {
|
6
|
+
if (BABYLON_LOADERS.OBJFileLoader != void 0 && !BABYLON_LOADERS.OBJFileLoader.IMPORT_VERTEX_COLORS) {
|
7
|
+
BABYLON_LOADERS.OBJFileLoader.IMPORT_VERTEX_COLORS = true;
|
8
|
+
}
|
9
|
+
}
|
10
|
+
export let value;
|
11
|
+
export let display_mode;
|
12
|
+
export let clear_color;
|
13
|
+
export let camera_position;
|
14
|
+
export let zoom_speed;
|
15
|
+
export let pan_speed;
|
16
|
+
$:
|
17
|
+
url = value.url;
|
18
|
+
export let resolved_url = void 0;
|
19
|
+
let latest_url;
|
20
|
+
$: {
|
21
|
+
resolved_url = url;
|
22
|
+
if (url) {
|
23
|
+
latest_url = url;
|
24
|
+
const resolving_url = url;
|
25
|
+
resolve_wasm_src(url).then((resolved) => {
|
26
|
+
if (latest_url === resolving_url) {
|
27
|
+
resolved_url = resolved ?? void 0;
|
28
|
+
} else {
|
29
|
+
resolved && URL.revokeObjectURL(resolved);
|
30
|
+
}
|
31
|
+
});
|
32
|
+
}
|
33
|
+
}
|
34
|
+
let canvas;
|
35
|
+
let scene;
|
36
|
+
let engine;
|
37
|
+
let point_cloud_system = null;
|
38
|
+
let mounted = false;
|
39
|
+
onMount(() => {
|
40
|
+
engine = new BABYLON.Engine(canvas, true);
|
41
|
+
scene = new BABYLON.Scene(engine);
|
42
|
+
scene.createDefaultCameraOrLight();
|
43
|
+
scene.useRightHandedSystem = true;
|
44
|
+
scene.clearColor = scene.clearColor = new BABYLON.Color4(...clear_color);
|
45
|
+
engine.runRenderLoop(() => {
|
46
|
+
scene.render();
|
47
|
+
});
|
48
|
+
function onWindowResize() {
|
49
|
+
engine.resize();
|
50
|
+
}
|
51
|
+
window.addEventListener("resize", onWindowResize);
|
52
|
+
mounted = true;
|
53
|
+
return () => {
|
54
|
+
scene.dispose();
|
55
|
+
engine.dispose();
|
56
|
+
window.removeEventListener("resize", onWindowResize);
|
57
|
+
};
|
58
|
+
});
|
59
|
+
$:
|
60
|
+
mounted && load_model(resolved_url);
|
61
|
+
function load_model(url2) {
|
62
|
+
if (scene) {
|
63
|
+
scene.meshes.forEach((mesh) => {
|
64
|
+
mesh.dispose();
|
65
|
+
});
|
66
|
+
if (point_cloud_system) {
|
67
|
+
point_cloud_system.dispose();
|
68
|
+
point_cloud_system = null;
|
69
|
+
}
|
70
|
+
if (url2) {
|
71
|
+
BABYLON.SceneLoader.ShowLoadingScreen = false;
|
72
|
+
BABYLON.SceneLoader.Append(
|
73
|
+
url2,
|
74
|
+
"",
|
75
|
+
scene,
|
76
|
+
() => {
|
77
|
+
if (display_mode === "point_cloud") {
|
78
|
+
create_point_cloud(scene);
|
79
|
+
} else if (display_mode === "wireframe") {
|
80
|
+
create_wireframe(scene);
|
81
|
+
} else {
|
82
|
+
create_camera(scene, camera_position, zoom_speed, pan_speed);
|
83
|
+
}
|
84
|
+
},
|
85
|
+
void 0,
|
86
|
+
void 0,
|
87
|
+
"." + value.path.split(".").pop()
|
88
|
+
);
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
function create_camera(scene2, camera_position2, zoom_speed2, pan_speed2) {
|
93
|
+
scene2.createDefaultCamera(true, true, true);
|
94
|
+
var helperCamera = scene2.activeCamera;
|
95
|
+
if (camera_position2[0] !== null) {
|
96
|
+
helperCamera.alpha = BABYLON.Tools.ToRadians(camera_position2[0]);
|
97
|
+
}
|
98
|
+
if (camera_position2[1] !== null) {
|
99
|
+
helperCamera.beta = BABYLON.Tools.ToRadians(camera_position2[1]);
|
100
|
+
}
|
101
|
+
if (camera_position2[2] !== null) {
|
102
|
+
helperCamera.radius = camera_position2[2];
|
103
|
+
}
|
104
|
+
helperCamera.lowerRadiusLimit = 0.1;
|
105
|
+
const updateCameraSensibility = () => {
|
106
|
+
helperCamera.wheelPrecision = 250 / (helperCamera.radius * zoom_speed2);
|
107
|
+
helperCamera.panningSensibility = 1e4 * pan_speed2 / helperCamera.radius;
|
108
|
+
};
|
109
|
+
updateCameraSensibility();
|
110
|
+
helperCamera.attachControl(true);
|
111
|
+
helperCamera.onAfterCheckInputsObservable.add(updateCameraSensibility);
|
112
|
+
}
|
113
|
+
export function reset_camera_position(camera_position2, zoom_speed2, pan_speed2) {
|
114
|
+
if (scene) {
|
115
|
+
scene.removeCamera(scene.activeCamera);
|
116
|
+
create_camera(scene, camera_position2, zoom_speed2, pan_speed2);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
function create_point_cloud(scene2) {
|
120
|
+
const meshes = scene2.meshes;
|
121
|
+
const pointPositions = [];
|
122
|
+
meshes.forEach((mesh) => {
|
123
|
+
if (mesh instanceof BABYLON.Mesh) {
|
124
|
+
const positions = mesh.getVerticesData(
|
125
|
+
BABYLON.VertexBuffer.PositionKind
|
126
|
+
);
|
127
|
+
if (positions) {
|
128
|
+
for (let i = 0; i < positions.length; i += 3) {
|
129
|
+
pointPositions.push(
|
130
|
+
new BABYLON.Vector3(
|
131
|
+
positions[i],
|
132
|
+
positions[i + 1],
|
133
|
+
positions[i + 2]
|
134
|
+
)
|
135
|
+
);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
mesh.setEnabled(false);
|
139
|
+
}
|
140
|
+
});
|
141
|
+
point_cloud_system = new BABYLON.PointsCloudSystem(
|
142
|
+
"point_cloud_system",
|
143
|
+
1,
|
144
|
+
scene2
|
145
|
+
);
|
146
|
+
point_cloud_system.addPoints(
|
147
|
+
pointPositions.length,
|
148
|
+
(particle, i) => {
|
149
|
+
particle.position = pointPositions[i];
|
150
|
+
particle.color = new BABYLON.Color4(
|
151
|
+
Math.random(),
|
152
|
+
Math.random(),
|
153
|
+
Math.random(),
|
154
|
+
1
|
155
|
+
);
|
156
|
+
}
|
157
|
+
);
|
158
|
+
point_cloud_system.buildMeshAsync().then((mesh) => {
|
159
|
+
mesh.alwaysSelectAsActiveMesh = true;
|
160
|
+
create_camera(scene2, camera_position, zoom_speed, pan_speed);
|
161
|
+
});
|
162
|
+
}
|
163
|
+
function create_wireframe(scene2) {
|
164
|
+
scene2.meshes.forEach((mesh) => {
|
165
|
+
if (mesh instanceof BABYLON.Mesh) {
|
166
|
+
mesh.material = new BABYLON.StandardMaterial(
|
167
|
+
"wireframeMaterial",
|
168
|
+
scene2
|
169
|
+
);
|
170
|
+
mesh.material.wireframe = true;
|
171
|
+
}
|
172
|
+
create_camera(scene2, camera_position, zoom_speed, pan_speed);
|
173
|
+
});
|
174
|
+
}
|
175
|
+
</script>
|
176
|
+
|
177
|
+
<canvas bind:this={canvas}></canvas>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import type { FileData } from "@gradio/client";
|
3
|
+
declare const __propDef: {
|
4
|
+
props: {
|
5
|
+
value: FileData;
|
6
|
+
display_mode: "solid" | "point_cloud" | "wireframe";
|
7
|
+
clear_color: [number, number, number, number];
|
8
|
+
camera_position: [number | null, number | null, number | null];
|
9
|
+
zoom_speed: number;
|
10
|
+
pan_speed: number;
|
11
|
+
resolved_url?: string | undefined;
|
12
|
+
reset_camera_position?: ((camera_position: [number | null, number | null, number | null], zoom_speed: number, pan_speed: number) => void) | undefined;
|
13
|
+
};
|
14
|
+
events: {
|
15
|
+
[evt: string]: CustomEvent<any>;
|
16
|
+
};
|
17
|
+
slots: {};
|
18
|
+
};
|
19
|
+
export type Canvas3DProps = typeof __propDef.props;
|
20
|
+
export type Canvas3DEvents = typeof __propDef.events;
|
21
|
+
export type Canvas3DSlots = typeof __propDef.slots;
|
22
|
+
export default class Canvas3D extends SvelteComponent<Canvas3DProps, Canvas3DEvents, Canvas3DSlots> {
|
23
|
+
get reset_camera_position(): (camera_position: [number | null, number | null, number | null], zoom_speed: number, pan_speed: number) => void;
|
24
|
+
}
|
25
|
+
export {};
|
@@ -0,0 +1,103 @@
|
|
1
|
+
<script>import { onMount } from "svelte";
|
2
|
+
import * as SPLAT from "gsplat";
|
3
|
+
import { resolve_wasm_src } from "@gradio/wasm/svelte";
|
4
|
+
export let value;
|
5
|
+
export let zoom_speed;
|
6
|
+
export let pan_speed;
|
7
|
+
$:
|
8
|
+
url = value.url;
|
9
|
+
export let resolved_url = void 0;
|
10
|
+
let latest_url;
|
11
|
+
$: {
|
12
|
+
resolved_url = url;
|
13
|
+
if (url) {
|
14
|
+
latest_url = url;
|
15
|
+
const resolving_url = url;
|
16
|
+
resolve_wasm_src(url).then((resolved) => {
|
17
|
+
if (latest_url === resolving_url) {
|
18
|
+
resolved_url = resolved ?? void 0;
|
19
|
+
} else {
|
20
|
+
resolved && URL.revokeObjectURL(resolved);
|
21
|
+
}
|
22
|
+
});
|
23
|
+
}
|
24
|
+
}
|
25
|
+
let canvas;
|
26
|
+
let scene;
|
27
|
+
let camera;
|
28
|
+
let renderer = null;
|
29
|
+
let controls;
|
30
|
+
let mounted = false;
|
31
|
+
let frameId = null;
|
32
|
+
function reset_scene() {
|
33
|
+
if (frameId !== null) {
|
34
|
+
cancelAnimationFrame(frameId);
|
35
|
+
frameId = null;
|
36
|
+
}
|
37
|
+
if (renderer !== null) {
|
38
|
+
renderer.dispose();
|
39
|
+
renderer = null;
|
40
|
+
}
|
41
|
+
scene = new SPLAT.Scene();
|
42
|
+
camera = new SPLAT.Camera();
|
43
|
+
renderer = new SPLAT.WebGLRenderer(canvas);
|
44
|
+
controls = new SPLAT.OrbitControls(camera, canvas);
|
45
|
+
controls.zoomSpeed = zoom_speed;
|
46
|
+
controls.panSpeed = pan_speed;
|
47
|
+
if (!value) {
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
let loading = false;
|
51
|
+
const load = async () => {
|
52
|
+
if (loading) {
|
53
|
+
console.error("Already loading");
|
54
|
+
return;
|
55
|
+
}
|
56
|
+
if (!resolved_url) {
|
57
|
+
throw new Error("No resolved URL");
|
58
|
+
}
|
59
|
+
loading = true;
|
60
|
+
if (resolved_url.endsWith(".ply")) {
|
61
|
+
await SPLAT.PLYLoader.LoadAsync(resolved_url, scene, void 0);
|
62
|
+
} else if (resolved_url.endsWith(".splat")) {
|
63
|
+
await SPLAT.Loader.LoadAsync(resolved_url, scene, void 0);
|
64
|
+
} else {
|
65
|
+
throw new Error("Unsupported file type");
|
66
|
+
}
|
67
|
+
loading = false;
|
68
|
+
};
|
69
|
+
const frame = () => {
|
70
|
+
if (!renderer) {
|
71
|
+
return;
|
72
|
+
}
|
73
|
+
if (loading) {
|
74
|
+
frameId = requestAnimationFrame(frame);
|
75
|
+
return;
|
76
|
+
}
|
77
|
+
controls.update();
|
78
|
+
renderer.render(scene, camera);
|
79
|
+
frameId = requestAnimationFrame(frame);
|
80
|
+
};
|
81
|
+
load();
|
82
|
+
frameId = requestAnimationFrame(frame);
|
83
|
+
}
|
84
|
+
onMount(() => {
|
85
|
+
if (value != null) {
|
86
|
+
reset_scene();
|
87
|
+
}
|
88
|
+
mounted = true;
|
89
|
+
return () => {
|
90
|
+
if (renderer) {
|
91
|
+
renderer.dispose();
|
92
|
+
}
|
93
|
+
};
|
94
|
+
});
|
95
|
+
$:
|
96
|
+
({ path } = value || {
|
97
|
+
path: void 0
|
98
|
+
});
|
99
|
+
$:
|
100
|
+
canvas && mounted && path && reset_scene();
|
101
|
+
</script>
|
102
|
+
|
103
|
+
<canvas bind:this={canvas}></canvas>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import type { FileData } from "@gradio/client";
|
3
|
+
declare const __propDef: {
|
4
|
+
props: {
|
5
|
+
value: FileData;
|
6
|
+
zoom_speed: number;
|
7
|
+
pan_speed: number;
|
8
|
+
resolved_url?: string | undefined;
|
9
|
+
};
|
10
|
+
events: {
|
11
|
+
[evt: string]: CustomEvent<any>;
|
12
|
+
};
|
13
|
+
slots: {};
|
14
|
+
};
|
15
|
+
export type Canvas3DgsProps = typeof __propDef.props;
|
16
|
+
export type Canvas3DgsEvents = typeof __propDef.events;
|
17
|
+
export type Canvas3DgsSlots = typeof __propDef.slots;
|
18
|
+
export default class Canvas3Dgs extends SvelteComponent<Canvas3DgsProps, Canvas3DgsEvents, Canvas3DgsSlots> {
|
19
|
+
}
|
20
|
+
export {};
|
@@ -0,0 +1,124 @@
|
|
1
|
+
<script>import { BlockLabel, IconButton } from "@gradio/atoms";
|
2
|
+
import { File, Download, Undo } from "@gradio/icons";
|
3
|
+
import { dequal } from "dequal";
|
4
|
+
export let value;
|
5
|
+
export let display_mode = "solid";
|
6
|
+
export let clear_color = [0, 0, 0, 0];
|
7
|
+
export let label = "";
|
8
|
+
export let show_label;
|
9
|
+
export let i18n;
|
10
|
+
export let zoom_speed = 1;
|
11
|
+
export let pan_speed = 1;
|
12
|
+
export let camera_position = [
|
13
|
+
null,
|
14
|
+
null,
|
15
|
+
null
|
16
|
+
];
|
17
|
+
let current_settings = { camera_position, zoom_speed, pan_speed };
|
18
|
+
let use_3dgs = false;
|
19
|
+
let Canvas3DGSComponent;
|
20
|
+
let Canvas3DComponent;
|
21
|
+
async function loadCanvas3D() {
|
22
|
+
const module = await import("./Canvas3D.svelte");
|
23
|
+
return module.default;
|
24
|
+
}
|
25
|
+
async function loadCanvas3DGS() {
|
26
|
+
const module = await import("./Canvas3DGS.svelte");
|
27
|
+
return module.default;
|
28
|
+
}
|
29
|
+
$:
|
30
|
+
if (value) {
|
31
|
+
use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
|
32
|
+
if (use_3dgs) {
|
33
|
+
loadCanvas3DGS().then((component) => {
|
34
|
+
Canvas3DGSComponent = component;
|
35
|
+
});
|
36
|
+
} else {
|
37
|
+
loadCanvas3D().then((component) => {
|
38
|
+
Canvas3DComponent = component;
|
39
|
+
});
|
40
|
+
}
|
41
|
+
}
|
42
|
+
let canvas3d;
|
43
|
+
function handle_undo() {
|
44
|
+
canvas3d?.reset_camera_position(camera_position, zoom_speed, pan_speed);
|
45
|
+
}
|
46
|
+
$: {
|
47
|
+
if (!dequal(current_settings.camera_position, camera_position) || current_settings.zoom_speed !== zoom_speed || current_settings.pan_speed !== pan_speed) {
|
48
|
+
canvas3d?.reset_camera_position(camera_position, zoom_speed, pan_speed);
|
49
|
+
current_settings = { camera_position, zoom_speed, pan_speed };
|
50
|
+
}
|
51
|
+
}
|
52
|
+
let resolved_url;
|
53
|
+
</script>
|
54
|
+
|
55
|
+
<BlockLabel
|
56
|
+
{show_label}
|
57
|
+
Icon={File}
|
58
|
+
label={label || i18n("3D_model.3d_model")}
|
59
|
+
/>
|
60
|
+
{#if value}
|
61
|
+
<div class="model3D">
|
62
|
+
<div class="buttons">
|
63
|
+
{#if !use_3dgs}
|
64
|
+
<!-- Canvas3DGS doesn't implement the undo method (reset_camera_position) -->
|
65
|
+
<IconButton Icon={Undo} label="Undo" on:click={() => handle_undo()} />
|
66
|
+
{/if}
|
67
|
+
<a
|
68
|
+
href={resolved_url}
|
69
|
+
target={window.__is_colab__ ? "_blank" : null}
|
70
|
+
download={window.__is_colab__ ? null : value.orig_name || value.path}
|
71
|
+
>
|
72
|
+
<IconButton Icon={Download} label={i18n("common.download")} />
|
73
|
+
</a>
|
74
|
+
</div>
|
75
|
+
|
76
|
+
{#if use_3dgs}
|
77
|
+
<svelte:component
|
78
|
+
this={Canvas3DGSComponent}
|
79
|
+
bind:resolved_url
|
80
|
+
{value}
|
81
|
+
{zoom_speed}
|
82
|
+
{pan_speed}
|
83
|
+
/>
|
84
|
+
{:else}
|
85
|
+
<svelte:component
|
86
|
+
this={Canvas3DComponent}
|
87
|
+
bind:this={canvas3d}
|
88
|
+
bind:resolved_url
|
89
|
+
{value}
|
90
|
+
{display_mode}
|
91
|
+
{clear_color}
|
92
|
+
{camera_position}
|
93
|
+
{zoom_speed}
|
94
|
+
{pan_speed}
|
95
|
+
/>
|
96
|
+
{/if}
|
97
|
+
</div>
|
98
|
+
{/if}
|
99
|
+
|
100
|
+
<style>
|
101
|
+
.model3D {
|
102
|
+
display: flex;
|
103
|
+
position: relative;
|
104
|
+
width: var(--size-full);
|
105
|
+
height: var(--size-full);
|
106
|
+
border-radius: var(--block-radius);
|
107
|
+
overflow: hidden;
|
108
|
+
}
|
109
|
+
.model3D :global(canvas) {
|
110
|
+
width: var(--size-full);
|
111
|
+
height: var(--size-full);
|
112
|
+
object-fit: contain;
|
113
|
+
overflow: hidden;
|
114
|
+
}
|
115
|
+
.buttons {
|
116
|
+
display: flex;
|
117
|
+
position: absolute;
|
118
|
+
top: var(--size-2);
|
119
|
+
right: var(--size-2);
|
120
|
+
justify-content: flex-end;
|
121
|
+
gap: var(--spacing-sm);
|
122
|
+
z-index: var(--layer-5);
|
123
|
+
}
|
124
|
+
</style>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import type { FileData } from "@gradio/client";
|
3
|
+
import type { I18nFormatter } from "@gradio/utils";
|
4
|
+
declare const __propDef: {
|
5
|
+
props: {
|
6
|
+
value: FileData | null;
|
7
|
+
display_mode?: ("solid" | "point_cloud" | "wireframe") | undefined;
|
8
|
+
clear_color?: [number, number, number, number] | undefined;
|
9
|
+
label?: string | undefined;
|
10
|
+
show_label: boolean;
|
11
|
+
i18n: I18nFormatter;
|
12
|
+
zoom_speed?: number | undefined;
|
13
|
+
pan_speed?: number | undefined;
|
14
|
+
camera_position?: [number | null, number | null, number | null] | undefined;
|
15
|
+
};
|
16
|
+
events: {
|
17
|
+
[evt: string]: CustomEvent<any>;
|
18
|
+
};
|
19
|
+
slots: {};
|
20
|
+
};
|
21
|
+
export type Model3DProps = typeof __propDef.props;
|
22
|
+
export type Model3DEvents = typeof __propDef.events;
|
23
|
+
export type Model3DSlots = typeof __propDef.slots;
|
24
|
+
export default class Model3D extends SvelteComponent<Model3DProps, Model3DEvents, Model3DSlots> {
|
25
|
+
}
|
26
|
+
export {};
|
@@ -0,0 +1,135 @@
|
|
1
|
+
<script>import { createEventDispatcher, tick } from "svelte";
|
2
|
+
import { Upload, ModifyUpload } from "@gradio/upload";
|
3
|
+
import { BlockLabel } from "@gradio/atoms";
|
4
|
+
import { File } from "@gradio/icons";
|
5
|
+
export let value;
|
6
|
+
export let display_mode = "solid";
|
7
|
+
export let clear_color = [0, 0, 0, 0];
|
8
|
+
export let label = "";
|
9
|
+
export let show_label;
|
10
|
+
export let root;
|
11
|
+
export let i18n;
|
12
|
+
export let zoom_speed = 1;
|
13
|
+
export let pan_speed = 1;
|
14
|
+
export let max_file_size = null;
|
15
|
+
export let camera_position = [
|
16
|
+
null,
|
17
|
+
null,
|
18
|
+
null
|
19
|
+
];
|
20
|
+
export let upload;
|
21
|
+
export let stream_handler;
|
22
|
+
async function handle_upload({
|
23
|
+
detail
|
24
|
+
}) {
|
25
|
+
value = detail;
|
26
|
+
await tick();
|
27
|
+
dispatch("change", value);
|
28
|
+
dispatch("load", value);
|
29
|
+
}
|
30
|
+
async function handle_clear() {
|
31
|
+
value = null;
|
32
|
+
await tick();
|
33
|
+
dispatch("clear");
|
34
|
+
dispatch("change");
|
35
|
+
}
|
36
|
+
let use_3dgs = false;
|
37
|
+
let Canvas3DGSComponent;
|
38
|
+
let Canvas3DComponent;
|
39
|
+
async function loadCanvas3D() {
|
40
|
+
const module = await import("./Canvas3D.svelte");
|
41
|
+
return module.default;
|
42
|
+
}
|
43
|
+
async function loadCanvas3DGS() {
|
44
|
+
const module = await import("./Canvas3DGS.svelte");
|
45
|
+
return module.default;
|
46
|
+
}
|
47
|
+
$:
|
48
|
+
if (value) {
|
49
|
+
use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
|
50
|
+
if (use_3dgs) {
|
51
|
+
loadCanvas3DGS().then((component) => {
|
52
|
+
Canvas3DGSComponent = component;
|
53
|
+
});
|
54
|
+
} else {
|
55
|
+
loadCanvas3D().then((component) => {
|
56
|
+
Canvas3DComponent = component;
|
57
|
+
});
|
58
|
+
}
|
59
|
+
}
|
60
|
+
let canvas3d;
|
61
|
+
async function handle_undo() {
|
62
|
+
canvas3d?.reset_camera_position(camera_position, zoom_speed, pan_speed);
|
63
|
+
}
|
64
|
+
const dispatch = createEventDispatcher();
|
65
|
+
let dragging = false;
|
66
|
+
$:
|
67
|
+
dispatch("drag", dragging);
|
68
|
+
</script>
|
69
|
+
|
70
|
+
<BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
|
71
|
+
|
72
|
+
{#if value === null}
|
73
|
+
<Upload
|
74
|
+
{upload}
|
75
|
+
{stream_handler}
|
76
|
+
on:load={handle_upload}
|
77
|
+
{root}
|
78
|
+
{max_file_size}
|
79
|
+
filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj", ".splat", ".ply"]}
|
80
|
+
bind:dragging
|
81
|
+
on:error
|
82
|
+
>
|
83
|
+
<slot />
|
84
|
+
</Upload>
|
85
|
+
{:else}
|
86
|
+
<div class="input-model">
|
87
|
+
<ModifyUpload
|
88
|
+
undoable={!use_3dgs}
|
89
|
+
on:clear={handle_clear}
|
90
|
+
{i18n}
|
91
|
+
on:undo={handle_undo}
|
92
|
+
absolute
|
93
|
+
/>
|
94
|
+
|
95
|
+
{#if use_3dgs}
|
96
|
+
<svelte:component
|
97
|
+
this={Canvas3DGSComponent}
|
98
|
+
{value}
|
99
|
+
{zoom_speed}
|
100
|
+
{pan_speed}
|
101
|
+
/>
|
102
|
+
{:else}
|
103
|
+
<svelte:component
|
104
|
+
this={Canvas3DComponent}
|
105
|
+
bind:this={canvas3d}
|
106
|
+
{value}
|
107
|
+
{display_mode}
|
108
|
+
{clear_color}
|
109
|
+
{camera_position}
|
110
|
+
{zoom_speed}
|
111
|
+
{pan_speed}
|
112
|
+
/>
|
113
|
+
{/if}
|
114
|
+
</div>
|
115
|
+
{/if}
|
116
|
+
|
117
|
+
<style>
|
118
|
+
.input-model {
|
119
|
+
display: flex;
|
120
|
+
position: relative;
|
121
|
+
justify-content: center;
|
122
|
+
align-items: center;
|
123
|
+
width: var(--size-full);
|
124
|
+
height: var(--size-full);
|
125
|
+
border-radius: var(--block-radius);
|
126
|
+
overflow: hidden;
|
127
|
+
}
|
128
|
+
|
129
|
+
.input-model :global(canvas) {
|
130
|
+
width: var(--size-full);
|
131
|
+
height: var(--size-full);
|
132
|
+
object-fit: contain;
|
133
|
+
overflow: hidden;
|
134
|
+
}
|
135
|
+
</style>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import type { FileData, Client } from "@gradio/client";
|
3
|
+
import type { I18nFormatter } from "@gradio/utils";
|
4
|
+
declare const __propDef: {
|
5
|
+
props: {
|
6
|
+
value: null | FileData;
|
7
|
+
display_mode?: ("solid" | "point_cloud" | "wireframe") | undefined;
|
8
|
+
clear_color?: [number, number, number, number] | undefined;
|
9
|
+
label?: string | undefined;
|
10
|
+
show_label: boolean;
|
11
|
+
root: string;
|
12
|
+
i18n: I18nFormatter;
|
13
|
+
zoom_speed?: number | undefined;
|
14
|
+
pan_speed?: number | undefined;
|
15
|
+
max_file_size?: (number | null) | undefined;
|
16
|
+
camera_position?: [number | null, number | null, number | null] | undefined;
|
17
|
+
upload: Client["upload"];
|
18
|
+
stream_handler: Client["stream"];
|
19
|
+
};
|
20
|
+
events: {
|
21
|
+
error: CustomEvent<any>;
|
22
|
+
change: CustomEvent<FileData | null>;
|
23
|
+
clear: CustomEvent<undefined>;
|
24
|
+
drag: CustomEvent<boolean>;
|
25
|
+
load: CustomEvent<FileData>;
|
26
|
+
} & {
|
27
|
+
[evt: string]: CustomEvent<any>;
|
28
|
+
};
|
29
|
+
slots: {
|
30
|
+
default: {};
|
31
|
+
};
|
32
|
+
};
|
33
|
+
export type Model3DUploadProps = typeof __propDef.props;
|
34
|
+
export type Model3DUploadEvents = typeof __propDef.events;
|
35
|
+
export type Model3DUploadSlots = typeof __propDef.slots;
|
36
|
+
export default class Model3DUpload extends SvelteComponent<Model3DUploadProps, Model3DUploadEvents, Model3DUploadSlots> {
|
37
|
+
}
|
38
|
+
export {};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@gradio/model3d",
|
3
|
-
"version": "0.12.
|
3
|
+
"version": "0.12.2-beta.1",
|
4
4
|
"description": "Gradio UI packages",
|
5
5
|
"type": "module",
|
6
6
|
"author": "",
|
@@ -12,22 +12,38 @@
|
|
12
12
|
"babylonjs-loaders": "^4.2.1",
|
13
13
|
"dequal": "^2.0.2",
|
14
14
|
"gsplat": "^1.0.5",
|
15
|
-
"@gradio/atoms": "^0.
|
16
|
-
"@gradio/
|
17
|
-
"@gradio/statustracker": "^0.
|
18
|
-
"@gradio/
|
19
|
-
"@gradio/
|
20
|
-
"@gradio/
|
21
|
-
"@gradio/
|
15
|
+
"@gradio/atoms": "^0.8.1-beta.1",
|
16
|
+
"@gradio/icons": "^0.8.0-beta.1",
|
17
|
+
"@gradio/statustracker": "^0.8.0-beta.1",
|
18
|
+
"@gradio/upload": "^0.12.4-beta.1",
|
19
|
+
"@gradio/wasm": "^0.13.1-beta.1",
|
20
|
+
"@gradio/utils": "^0.7.0-beta.1",
|
21
|
+
"@gradio/client": "^1.6.0-beta.1"
|
22
22
|
},
|
23
23
|
"devDependencies": {
|
24
|
-
"@gradio/preview": "^0.
|
24
|
+
"@gradio/preview": "^0.11.1-beta.0"
|
25
25
|
},
|
26
26
|
"main_changeset": true,
|
27
27
|
"main": "./Index.svelte",
|
28
28
|
"exports": {
|
29
|
-
".":
|
30
|
-
|
29
|
+
".": {
|
30
|
+
"gradio": "./Index.svelte",
|
31
|
+
"svelte": "./dist/Index.svelte",
|
32
|
+
"types": "./dist/Index.svelte.d.ts"
|
33
|
+
},
|
34
|
+
"./example": {
|
35
|
+
"gradio": "./Example.svelte",
|
36
|
+
"svelte": "./dist/Example.svelte",
|
37
|
+
"types": "./dist/Example.svelte.d.ts"
|
38
|
+
},
|
31
39
|
"./package.json": "./package.json"
|
40
|
+
},
|
41
|
+
"peerDependencies": {
|
42
|
+
"svelte": "^4.0.0"
|
43
|
+
},
|
44
|
+
"repository": {
|
45
|
+
"type": "git",
|
46
|
+
"url": "git+https://github.com/gradio-app/gradio.git",
|
47
|
+
"directory": "js/model3D"
|
32
48
|
}
|
33
49
|
}
|