-
Notifications
You must be signed in to change notification settings - Fork 2
feat(save_and_load): add import/export methods #249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 72 commits
aa86c91
5b2b4f3
f4ef491
5f3e0c6
e6c54b5
01a9732
0b70454
d577721
df419ec
bb9d02d
a93225c
af41f9d
ea63e2b
97072b5
63464b7
98991cf
6321c8f
35c75dd
22a67fe
caf5a39
18fc885
d4a1cd6
8aec8b8
6a282e4
196be52
fdf547e
c208b30
3ef456f
2ca553b
9fd6c0b
3806d54
3d04f42
24eb5b6
9866872
f01890f
344a873
019fcd4
e512f73
f3140b6
e251104
dbf0a99
a5b1ebf
63f07e2
b1a9353
7b57603
9ae7357
1535343
bbfc846
f6c1304
16b61de
bb5a485
779fbf9
2ad8f46
fdaca90
0802579
769c481
597ca03
5658e72
9eda52e
b686f44
7812125
e23b7e7
57e13f3
2375805
6c3c9cf
6136cde
eecd8b7
328eb14
0398bfe
7d392c0
5f65391
a1725f8
f6e73f2
309b5f4
cdbee18
7ab0049
7a0e43e
9954c87
933cb71
e0cf657
2f464bb
44a70fe
499fc4b
c0df47c
fca8a2a
28abd57
c156eab
e6d2c43
17607da
9853aab
5232228
6a61874
6e67d32
74d2ab5
5334a81
1ad0cae
a9222e8
c58d78d
f20ac1a
16a5aa9
10dcaad
c2859b1
dde3190
b213a23
edc66de
2c53c79
32611bb
3d89884
4978f96
60ef7d1
fb2818a
b53db94
4a88bda
c0419f9
bb9f00c
a8cc6a5
85bc68d
d88b39e
7ec0d6d
2811ff0
2707dc1
6972216
9b0efaf
d54d359
51a3ee9
fd9a30e
e5a95be
70afca8
f06c643
4f6941b
63c01a2
e60ef39
2e15441
6229593
522151b
47bb68b
59da2c4
2235337
5ecfd72
d819dee
559702e
941b3eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json" | ||
| import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" | ||
| import fileDownload from "js-file-download" | ||
|
|
||
| export function useProjectManager() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reported by reviewdog 🐶
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reported by reviewdog 🐶
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reported by reviewdog 🐶 |
||
| const geode = useGeodeStore() | ||
| const appStore = useAppStore() | ||
|
|
||
| async function exportProject() { | ||
| geode.start_request() | ||
MaxNumerique marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| try { | ||
| await useInfraStore().create_connection() | ||
| const snapshot = appStore.exportStores() | ||
| const schema = back_schemas.opengeodeweb_back.export_project | ||
| const defaultName = "project.zip" | ||
|
|
||
| await api_fetch( | ||
| { schema, params: { snapshot, filename: defaultName } }, | ||
| { | ||
| response_function: async (response) => { | ||
| const data = response._data | ||
| const downloadName = | ||
MaxNumerique marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| response.headers?.get?.("new-file-name") || defaultName | ||
| fileDownload(data, downloadName) | ||
| }, | ||
| }, | ||
| ) | ||
| } finally { | ||
| geode.stop_request() | ||
| } | ||
| } | ||
|
|
||
| async function importProjectFile(file) { | ||
| geode.start_request() | ||
MaxNumerique marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| try { | ||
| await useInfraStore().create_connection() | ||
|
|
||
| const schemaImport = back_schemas.opengeodeweb_back.import_project | ||
| const form = new FormData() | ||
| form.append("file", file, file?.name) | ||
|
|
||
| const result = await $fetch(schemaImport.$id, { | ||
| baseURL: geode.base_url, | ||
| method: "POST", | ||
| body: form, | ||
| }) | ||
| const snapshot = result?.snapshot ?? {} | ||
|
|
||
| await viewer_call({ | ||
| schema: viewer_schemas.opengeodeweb_viewer.import_project, | ||
| params: {}, | ||
| }) | ||
| await viewer_call({ | ||
| schema: viewer_schemas.opengeodeweb_viewer.viewer.reset_visualization, | ||
| params: {}, | ||
| }) | ||
|
|
||
| const treeviewStore = useTreeviewStore() | ||
| treeviewStore.isImporting = true | ||
| await treeviewStore.importStores(snapshot?.treeview) | ||
|
|
||
| const hybridViewerStore = useHybridViewerStore() | ||
| await hybridViewerStore.initHybridViewer() | ||
| hybridViewerStore.clear() | ||
| await hybridViewerStore.importStores(snapshot?.hybridViewer) | ||
|
|
||
| const snapshotDataBase = snapshot?.dataBase?.db || {} | ||
| const items = Object.entries(snapshotDataBase).map(([id, item]) => ({ | ||
| id, | ||
| object_type: item.object_type, | ||
| geode_object: item.geode_object, | ||
| native_filename: item.native_filename, | ||
| viewable_filename: item.viewable_filename, | ||
| displayed_name: item.displayed_name, | ||
| vtk_js: { binary_light_viewable: item?.vtk_js?.binary_light_viewable }, | ||
| })) | ||
|
|
||
| await importWorkflowFromSnapshot(items) | ||
|
|
||
| // Appliquer la caméra importée après avoir créé les actors | ||
| await hybridViewerStore.importStores(snapshot?.hybridViewer) | ||
|
|
||
| const dataStyleStore = useDataStyleStore() | ||
| await dataStyleStore.importStores(snapshot?.dataStyle) | ||
| await dataStyleStore.applyAllStylesFromState() | ||
|
|
||
| treeviewStore.finalizeImportSelection() | ||
| treeviewStore.isImporting = false | ||
| } finally { | ||
| geode.stop_request() | ||
| } | ||
| } | ||
|
|
||
| return { exportProject, importProjectFile } | ||
| } | ||
|
|
||
| export default useProjectManager | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reported by reviewdog 🐶 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,70 +1,214 @@ | ||
| // Third party imports | ||
| import "@kitware/vtk.js/Rendering/Profiles/Geometry" | ||
| import vtkGenericRenderWindow from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow" | ||
| import vtkXMLPolyDataReader from "@kitware/vtk.js/IO/XML/XMLPolyDataReader" | ||
| import vtkMapper from "@kitware/vtk.js/Rendering/Core/Mapper" | ||
| import vtkActor from "@kitware/vtk.js/Rendering/Core/Actor" | ||
|
|
||
| import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json" | ||
| import Status from "@ogw_f/utils/status.js" | ||
|
|
||
| export const useHybridViewerStore = defineStore("hybridViewer", () => { | ||
| const viewerStore = useViewerStore() | ||
| const dataBaseStore = useDataBaseStore() | ||
| const db = reactive({}) | ||
| const status = ref(Status.NOT_CREATED) | ||
| const camera_options = reactive({}) | ||
| const genericRenderWindow = reactive({}) | ||
| const is_moving = ref(false) | ||
| const zScale = ref(1.0) | ||
| let viewStream | ||
| let gridActor = null | ||
|
|
||
| async function initHybridViewer() { | ||
| if (status.value !== Status.NOT_CREATED) return | ||
| status.value = Status.CREATING | ||
| genericRenderWindow.value = vtkGenericRenderWindow.newInstance({ | ||
| background: [180 / 255, 180 / 255, 180 / 255], | ||
| listenWindowResize: false, | ||
| }) | ||
|
|
||
| // Local imports | ||
| import { useMeshPointsStyle } from "./points.js" | ||
| import { useMeshEdgesStyle } from "./edges.js" | ||
| import { useMeshPolygonsStyle } from "./polygons.js" | ||
| import { useMeshPolyhedraStyle } from "./polyhedra.js" | ||
| const webGLRenderWindow = | ||
| genericRenderWindow.value.getApiSpecificRenderWindow() | ||
| const imageStyle = webGLRenderWindow.getReferenceByName("bgImage").style | ||
| imageStyle.transition = "opacity 0.1s ease-in" | ||
| imageStyle.zIndex = 1 | ||
|
|
||
| // Local constants | ||
| const mesh_schemas = viewer_schemas.opengeodeweb_viewer.mesh | ||
| await viewerStore.ws_connect() | ||
| viewStream = viewerStore.client.getImageStream().createViewStream("-1") | ||
| viewStream.onImageReady((e) => { | ||
| if (is_moving.value) return | ||
| const webGLRenderWindow = | ||
| genericRenderWindow.value.getApiSpecificRenderWindow() | ||
| const imageStyle = webGLRenderWindow.getReferenceByName("bgImage").style | ||
| webGLRenderWindow.setBackgroundImage(e.image) | ||
| imageStyle.opacity = 1 | ||
| }) | ||
|
|
||
| export default function useMeshStyle() { | ||
| const dataStyleStore = useDataStyleStore() | ||
| const pointsStyleStore = useMeshPointsStyle() | ||
| const edgesStyleStore = useMeshEdgesStyle() | ||
| const meshPolygonsStyleStore = useMeshPolygonsStyle() | ||
| const meshPolyhedraStyleStore = useMeshPolyhedraStyle() | ||
| const hybridViewerStore = useHybridViewerStore() | ||
| status.value = Status.CREATED | ||
| } | ||
|
|
||
| async function addItem(id) { | ||
| if (!genericRenderWindow.value) { | ||
| return | ||
| } | ||
| const value = dataBaseStore.db[id] | ||
| console.log("hybridViewerStore.addItem", { value }) | ||
| const reader = vtkXMLPolyDataReader.newInstance() | ||
| const textEncoder = new TextEncoder() | ||
| await reader.parseAsArrayBuffer( | ||
| textEncoder.encode(value.vtk_js.binary_light_viewable), | ||
| ) | ||
| const polydata = reader.getOutputData(0) | ||
| const mapper = vtkMapper.newInstance() | ||
| mapper.setInputData(polydata) | ||
| const actor = vtkActor.newInstance() | ||
| actor.getProperty().setColor(20 / 255, 20 / 255, 20 / 255) | ||
| actor.setMapper(mapper) | ||
| const renderer = genericRenderWindow.value.getRenderer() | ||
| const renderWindow = genericRenderWindow.value.getRenderWindow() | ||
| renderer.addActor(actor) | ||
| renderer.resetCamera() | ||
| renderWindow.render() | ||
| db[id] = { actor, polydata, mapper } | ||
| } | ||
|
|
||
| function meshVisibility(id) { | ||
| return dataStyleStore.getStyle(id).visibility | ||
| async function setVisibility(id, visibility) { | ||
| db[id].actor.setVisibility(visibility) | ||
| const renderWindow = genericRenderWindow.value.getRenderWindow() | ||
| renderWindow.render() | ||
| } | ||
| async function setZScaling(z_scale) { | ||
| zScale.value = z_scale | ||
| const renderer = genericRenderWindow.value.getRenderer() | ||
| const actors = renderer.getActors() | ||
| actors.forEach((actor) => { | ||
| if (actor !== gridActor) { | ||
| const scale = actor.getScale() | ||
| actor.setScale(scale[0], scale[1], z_scale) | ||
| } | ||
| }) | ||
| renderer.resetCamera() | ||
| genericRenderWindow.value.getRenderWindow().render() | ||
| const schema = viewer_schemas?.opengeodeweb_viewer?.viewer?.set_z_scaling | ||
| if (!schema) return | ||
| await viewer_call({ | ||
| schema, | ||
| params: { | ||
| z_scale: z_scale, | ||
| }, | ||
| }) | ||
| } | ||
| function setMeshVisibility(id, visibility) { | ||
| return viewer_call( | ||
|
|
||
| function syncRemoteCamera() { | ||
| console.log("syncRemoteCamera") | ||
| const renderer = genericRenderWindow.value.getRenderer() | ||
| const camera = renderer.getActiveCamera() | ||
| const params = { | ||
| camera_options: { | ||
| focal_point: camera.getFocalPoint(), | ||
| view_up: camera.getViewUp(), | ||
| position: camera.getPosition(), | ||
| view_angle: camera.getViewAngle(), | ||
| clipping_range: camera.getClippingRange(), | ||
| distance: camera.getDistance(), | ||
| }, | ||
| } | ||
| viewer_call( | ||
| { | ||
| schema: mesh_schemas.visibility, | ||
| params: { id, visibility }, | ||
| schema: viewer_schemas.opengeodeweb_viewer.viewer.update_camera, | ||
| params, | ||
| }, | ||
| { | ||
| response_function: () => { | ||
| hybridViewerStore.setVisibility(id, visibility) | ||
| dataStyleStore.getStyle(id).visibility = visibility | ||
| console.log(setMeshVisibility.name, { id }, meshVisibility(id)) | ||
| remoteRender() | ||
| for (const key in params.camera_options) { | ||
| camera_options[key] = params.camera_options[key] | ||
| } | ||
| }, | ||
| }, | ||
| ) | ||
| } | ||
|
|
||
| function applyMeshStyle(id) { | ||
| const style = dataStyleStore.getStyle(id) | ||
| const promise_array = [] | ||
| for (const [key, value] of Object.entries(style)) { | ||
| if (key === "visibility") { | ||
| promise_array.push(setMeshVisibility(id, value)) | ||
| } else if (key === "points") { | ||
| promise_array.push(pointsStyleStore.applyMeshPointsStyle(id)) | ||
| } else if (key === "edges") { | ||
| promise_array.push(edgesStyleStore.applyMeshEdgesStyle(id)) | ||
| } else if (key === "polygons") { | ||
| promise_array.push(meshPolygonsStyleStore.applyMeshPolygonsStyle(id)) | ||
| } else if (key === "polyhedra") { | ||
| promise_array.push(meshPolyhedraStyleStore.applyMeshPolyhedraStyle(id)) | ||
| } else { | ||
| throw new Error("Unknown mesh key: " + key) | ||
| } | ||
| function remoteRender() { | ||
| viewer_call({ | ||
| schema: viewer_schemas.opengeodeweb_viewer.viewer.render, | ||
| }) | ||
| } | ||
|
|
||
| function setContainer(container) { | ||
| genericRenderWindow.value.setContainer(container.value.$el) | ||
| const webGLRenderWindow = | ||
| genericRenderWindow.value.getApiSpecificRenderWindow() | ||
| webGLRenderWindow.setUseBackgroundImage(true) | ||
| const imageStyle = webGLRenderWindow.getReferenceByName("bgImage").style | ||
| imageStyle.transition = "opacity 0.1s ease-in" | ||
| imageStyle.zIndex = 1 | ||
| resize(container.value.$el.offsetWidth, container.value.$el.offsetHeight) | ||
| console.log("setContainer", container.value.$el) | ||
|
|
||
| useMousePressed({ | ||
| target: container, | ||
| onPressed: (event) => { | ||
| console.log("onPressed") | ||
| if (event.button == 0) { | ||
| is_moving.value = true | ||
| event.stopPropagation() | ||
| imageStyle.opacity = 0 | ||
| } | ||
| }, | ||
| onReleased: () => { | ||
| if (!is_moving.value) { | ||
| return | ||
| } | ||
| is_moving.value = false | ||
| console.log("onReleased") | ||
| syncRemoteCamera() | ||
| }, | ||
| }) | ||
|
|
||
| let wheelEventEndTimeout = null | ||
| useEventListener(container, "wheel", () => { | ||
| is_moving.value = true | ||
| imageStyle.opacity = 0 | ||
| clearTimeout(wheelEventEndTimeout) | ||
| wheelEventEndTimeout = setTimeout(() => { | ||
| is_moving.value = false | ||
| syncRemoteCamera() | ||
| }, 600) | ||
| }) | ||
| } | ||
|
|
||
| async function resize(width, height) { | ||
| if ( | ||
| viewerStore.status !== Status.CONNECTED || | ||
| status.value !== Status.CREATED | ||
| ) { | ||
| return | ||
| } | ||
| return Promise.all(promise_array) | ||
| const webGLRenderWindow = | ||
| genericRenderWindow.value.getApiSpecificRenderWindow() | ||
| const canvas = webGLRenderWindow.getCanvas() | ||
| canvas.width = width | ||
| canvas.height = height | ||
| await nextTick() | ||
| webGLRenderWindow.setSize(width, height) | ||
| viewStream.setSize(width, height) | ||
| const renderWindow = genericRenderWindow.value.getRenderWindow() | ||
| renderWindow.render() | ||
| remoteRender() | ||
| } | ||
|
|
||
| return { | ||
| meshVisibility, | ||
| setMeshVisibility, | ||
| applyMeshStyle, | ||
| ...useMeshPointsStyle(), | ||
| ...useMeshEdgesStyle(), | ||
| ...useMeshPolygonsStyle(), | ||
| ...useMeshPolyhedraStyle(), | ||
| db, | ||
| genericRenderWindow, | ||
| addItem, | ||
| setVisibility, | ||
| setZScaling, | ||
| syncRemoteCamera, | ||
| initHybridViewer, | ||
| remoteRender, | ||
| resize, | ||
| setContainer, | ||
| zScale, | ||
| } | ||
| } | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JulienChampagnol que penses tu de cette proposition ?