mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
playground: most of the logic for both frontend and backend is implemented, some bugs remain
This commit is contained in:
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@@ -40,9 +40,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
- run: "SKIP_BUILD_FRONTEND=true poetry lock"
|
- run: "poetry lock"
|
||||||
- run: "SKIP_BUILD_FRONTEND=true poetry install"
|
- run: "poetry install"
|
||||||
- run: "SKIP_BUILD_FRONTEND=true poetry build"
|
- run: "poetry build" # Skips building frontend (not using task)
|
||||||
|
|
||||||
build-logo:
|
build-logo:
|
||||||
name: "Build logo"
|
name: "Build logo"
|
||||||
@@ -56,8 +56,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
- run: "SKIP_BUILD_FRONTEND=true poetry lock"
|
- run: "poetry lock"
|
||||||
- run: "SKIP_BUILD_FRONTEND=true poetry install"
|
- run: "poetry install"
|
||||||
- run: "poetry run python yacv_server/logo.py"
|
- run: "poetry run python yacv_server/logo.py"
|
||||||
- uses: "actions/upload-artifact@v4"
|
- uses: "actions/upload-artifact@v4"
|
||||||
with:
|
with:
|
||||||
@@ -77,8 +77,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
- run: "SKIP_BUILD_FRONTEND=true poetry lock"
|
- run: "poetry lock"
|
||||||
- run: "SKIP_BUILD_FRONTEND=true poetry install"
|
- run: "poetry install"
|
||||||
- run: "YACV_DISABLE_SERVER=true poetry run python example/object.py"
|
- run: "YACV_DISABLE_SERVER=true poetry run python example/object.py"
|
||||||
- uses: "actions/upload-artifact@v4"
|
- uses: "actions/upload-artifact@v4"
|
||||||
with:
|
with:
|
||||||
|
|||||||
9
.github/workflows/deploy2.yml
vendored
9
.github/workflows/deploy2.yml
vendored
@@ -35,11 +35,7 @@ jobs:
|
|||||||
- uses: "actions/download-artifact@v4"
|
- uses: "actions/download-artifact@v4"
|
||||||
with: # Downloads all artifacts from the build job
|
with: # Downloads all artifacts from the build job
|
||||||
path: "./public"
|
path: "./public"
|
||||||
- run: | # Merge the subdirectories of public into a single directory
|
merge-multiple: true
|
||||||
for dir in public/*; do
|
|
||||||
mv "$dir/"* public/
|
|
||||||
rmdir "$dir"
|
|
||||||
done
|
|
||||||
- uses: "actions/configure-pages@v5"
|
- uses: "actions/configure-pages@v5"
|
||||||
- uses: "actions/upload-pages-artifact@v3"
|
- uses: "actions/upload-pages-artifact@v3"
|
||||||
with:
|
with:
|
||||||
@@ -71,5 +67,6 @@ jobs:
|
|||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
- run: "poetry install"
|
- run: "poetry install"
|
||||||
- run: "poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}"
|
- run: "poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}"
|
||||||
- run: "poetry publish --build"
|
- run: "poetry run task build" # This task also builds the frontend (with reduced features for less size)
|
||||||
|
- run: "poetry publish"
|
||||||
|
|
||||||
|
|||||||
@@ -761,6 +761,36 @@ MIT License
|
|||||||
|
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
The following npm package may be included in this product:
|
||||||
|
|
||||||
|
- pako@2.1.0
|
||||||
|
|
||||||
|
This package contains the following license:
|
||||||
|
|
||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (C) 2014-2017 by Vitaly Puzrin and Andrei Tuputcyn
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
The following npm package may be included in this product:
|
The following npm package may be included in this product:
|
||||||
|
|
||||||
- lie@3.3.0
|
- lie@3.3.0
|
||||||
|
|||||||
9
build.py
9
build.py
@@ -1,9 +0,0 @@
|
|||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# Building the frontend is optional
|
|
||||||
if os.getenv('SKIP_BUILD_FRONTEND') is None and os.path.exists('package.json'):
|
|
||||||
# When building the backend, make sure the frontend is built first
|
|
||||||
subprocess.run(['yarn', 'install'], check=True)
|
|
||||||
subprocess.run(['yarn', 'build', '--outDir', 'yacv_server/frontend'], check=True)
|
|
||||||
@@ -13,6 +13,7 @@ import {Document} from "@gltf-transform/core";
|
|||||||
import type ModelViewerWrapperT from "./viewer/ModelViewerWrapper.vue";
|
import type ModelViewerWrapperT from "./viewer/ModelViewerWrapper.vue";
|
||||||
import {mdiPlus} from '@mdi/js'
|
import {mdiPlus} from '@mdi/js'
|
||||||
import SvgIcon from '@jamescoyle/vue-icon';
|
import SvgIcon from '@jamescoyle/vue-icon';
|
||||||
|
import {toBuffer} from "./misc/gltf.ts";
|
||||||
|
|
||||||
// NOTE: The ModelViewer library is big (THREE.js), so we split it and import it asynchronously
|
// NOTE: The ModelViewer library is big (THREE.js), so we split it and import it asynchronously
|
||||||
const ModelViewerWrapper = defineAsyncComponent({
|
const ModelViewerWrapper = defineAsyncComponent({
|
||||||
@@ -82,14 +83,24 @@ networkMgr.addEventListener('update-early',
|
|||||||
networkMgr.addEventListener('update', (e) => onModelUpdateRequest(e as NetworkUpdateEvent));
|
networkMgr.addEventListener('update', (e) => onModelUpdateRequest(e as NetworkUpdateEvent));
|
||||||
(async () => { // Start loading all configured models ASAP
|
(async () => { // Start loading all configured models ASAP
|
||||||
let sett = await settings();
|
let sett = await settings();
|
||||||
watch(viewer, (newViewer) => {
|
if (sett.preload.length > 0) {
|
||||||
if (newViewer) {
|
watch(viewer, (newViewer) => {
|
||||||
newViewer.setPosterText('<tspan x="50%" dy="1.2em">Trying to load' +
|
if (newViewer) {
|
||||||
' models from:</tspan>' + sett.preload.map((url: string) => '<tspan x="50%" dy="1.2em">- ' + url + '</tspan>').join(""));
|
newViewer.setPosterText('<tspan x="50%" dy="1.2em">Trying to load' +
|
||||||
|
' models from:</tspan>' + sett.preload.map((url: string) => '<tspan x="50%" dy="1.2em">- ' + url + '</tspan>').join(""));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (let model of sett.preload) {
|
||||||
|
await networkMgr.load(model);
|
||||||
}
|
}
|
||||||
});
|
} else { // Skip to interface without models (useful for playground mode)
|
||||||
for (let model of sett.preload) {
|
console.debug("Showing empty gltf document to load the interface without models.");
|
||||||
await networkMgr.load(model);
|
// FIXME: Empty document breaks the playground-loaded models (using preload seems to fix this, maybe __helpers issue?)
|
||||||
|
let emptyDoc = new Document();
|
||||||
|
emptyDoc.createScene();
|
||||||
|
let buffer = await toBuffer(emptyDoc);
|
||||||
|
let blob = new Blob([buffer], {type: 'model/gltf-binary'});
|
||||||
|
sceneUrl.value = URL.createObjectURL(blob);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -117,7 +128,7 @@ async function loadModelManual() {
|
|||||||
<svg-icon :path="mdiPlus" type="mdi"/>
|
<svg-icon :path="mdiPlus" type="mdi"/>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<models ref="models" :viewer="viewer" @remove="onModelRemoveRequest"/>
|
<models ref="models" :viewer="viewer" @remove-model="onModelRemoveRequest"/>
|
||||||
</sidebar>
|
</sidebar>
|
||||||
|
|
||||||
<!-- The right collapsible sidebar has the list of tools -->
|
<!-- The right collapsible sidebar has the list of tools -->
|
||||||
@@ -125,7 +136,7 @@ async function loadModelManual() {
|
|||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<v-toolbar-title>Tools</v-toolbar-title>
|
<v-toolbar-title>Tools</v-toolbar-title>
|
||||||
</template>
|
</template>
|
||||||
<tools ref="tools" :viewer="viewer" @findModel="(name) => models?.findModel(name)"/>
|
<tools ref="tools" :viewer="viewer" @find-model="models?.findModel" @update-model="onModelUpdateRequest"/>
|
||||||
</sidebar>
|
</sidebar>
|
||||||
|
|
||||||
</v-layout>
|
</v-layout>
|
||||||
@@ -135,6 +146,6 @@ async function loadModelManual() {
|
|||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
30
frontend/misc/IfNotSmallBuild.vue
Normal file
30
frontend/misc/IfNotSmallBuild.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {mdiLockQuestion} from "@mdi/js";
|
||||||
|
import {VBtn, VTooltip} from "vuetify/lib/components/index.mjs";
|
||||||
|
import SvgIcon from "@jamescoyle/vue-icon";
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
let isSmallBuild = typeof __YACV_SMALL_BUILD__ !== 'undefined' && __YACV_SMALL_BUILD__;
|
||||||
|
|
||||||
|
function clickedButton() { // Redirect to the main build
|
||||||
|
window.open("https://yeicor-3d.github.io/yet-another-cad-viewer/" + window.location.search + window.location.hash, '_blank');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- @ts-ignore-->
|
||||||
|
<!-- Include the children as this is a full build -->
|
||||||
|
<slot v-if="!isSmallBuild"/>
|
||||||
|
<!-- A small info button saying that a feature is missing, and linking to the main build -->
|
||||||
|
<v-btn v-else icon @click="clickedButton" base-color="#a00" style="margin: auto; display: block;">
|
||||||
|
<v-tooltip activator="parent">
|
||||||
|
This feature is not available in the small build.<br/>
|
||||||
|
Click to go to the main build.
|
||||||
|
</v-tooltip>
|
||||||
|
<svg-icon :path="mdiLockQuestion" type="mdi"/>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -5,6 +5,9 @@ let io = new WebIO();
|
|||||||
export let extrasNameKey = "__yacv_name";
|
export let extrasNameKey = "__yacv_name";
|
||||||
export let extrasNameValueHelpers = "__helpers";
|
export let extrasNameValueHelpers = "__helpers";
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
let isSmallBuild = typeof __YACV_SMALL_BUILD__ !== 'undefined' && __YACV_SMALL_BUILD__;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a GLB model from a URL and adds it to the document or replaces it if the names match.
|
* Loads a GLB model from a URL and adds it to the document or replaces it if the names match.
|
||||||
*
|
*
|
||||||
@@ -27,7 +30,7 @@ export async function mergePartial(url: string, name: string, document: Document
|
|||||||
try { // Try to load fast if no extensions are used
|
try { // Try to load fast if no extensions are used
|
||||||
newDoc = await io.readBinary(new Uint8Array(buffer));
|
newDoc = await io.readBinary(new Uint8Array(buffer));
|
||||||
} catch (e) { // Fallback to wait for download and register big extensions
|
} catch (e) { // Fallback to wait for download and register big extensions
|
||||||
if (e instanceof Error && e.message.toLowerCase().includes("khr_draco_mesh_compression")) {
|
if (!isSmallBuild && e instanceof Error && e.message.toLowerCase().includes("khr_draco_mesh_compression")) {
|
||||||
if (alreadyTried["draco"]) throw e; else alreadyTried["draco"] = true;
|
if (alreadyTried["draco"]) throw e; else alreadyTried["draco"] = true;
|
||||||
// WARNING: Draco decompression on web is really slow for non-trivial models! (it should work?)
|
// WARNING: Draco decompression on web is really slow for non-trivial models! (it should work?)
|
||||||
let {KHRDracoMeshCompression} = await import("@gltf-transform/extensions")
|
let {KHRDracoMeshCompression} = await import("@gltf-transform/extensions")
|
||||||
@@ -38,7 +41,7 @@ export async function mergePartial(url: string, name: string, document: Document
|
|||||||
'draco3d.decoder': await dracoDecoderWeb.default({}),
|
'draco3d.decoder': await dracoDecoderWeb.default({}),
|
||||||
'draco3d.encoder': await dracoEncoderWeb.default({})
|
'draco3d.encoder': await dracoEncoderWeb.default({})
|
||||||
});
|
});
|
||||||
} else if (e instanceof Error && e.message.toLowerCase().includes("ext_texture_webp")) {
|
} else if (!isSmallBuild && e instanceof Error && e.message.toLowerCase().includes("ext_texture_webp")) {
|
||||||
if (alreadyTried["webp"]) throw e; else alreadyTried["webp"] = true;
|
if (alreadyTried["webp"]) throw e; else alreadyTried["webp"] = true;
|
||||||
let {EXTTextureWebP} = await import("@gltf-transform/extensions")
|
let {EXTTextureWebP} = await import("@gltf-transform/extensions")
|
||||||
io.registerExtensions([EXTTextureWebP]);
|
io.registerExtensions([EXTTextureWebP]);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
// These are the default values for the settings, which are overridden below
|
// These are the default values for the settings, which are overridden below
|
||||||
|
import {ungzip} from "pako";
|
||||||
|
import {b66Decode} from "../tools/b66.ts";
|
||||||
|
|
||||||
let settingsCache: any = null;
|
let settingsCache: any = null;
|
||||||
|
|
||||||
export async function settings() {
|
export async function settings() {
|
||||||
@@ -49,10 +52,23 @@ export async function settings() {
|
|||||||
if (key in settings) (settings as any)[key] = parseSetting(key, value, settings);
|
if (key in settings) (settings as any)[key] = parseSetting(key, value, settings);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Auto-decompress the code
|
||||||
|
if (settings.code.length > 0) {
|
||||||
|
try {
|
||||||
|
settings.code = ungzip(b66Decode(settings.code), {to: 'string'});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Failed to decompress code (assuming raw code):", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the default preload URL if not overridden (requires a fetch that is avoided if possible)
|
// Get the default preload URL if not overridden (requires a fetch that is avoided if possible)
|
||||||
for (let i = 0; i < settings.preload.length; i++) {
|
for (let i = 0; i < settings.preload.length; i++) {
|
||||||
let url = settings.preload[i];
|
let url = settings.preload[i];
|
||||||
if (url === '<auto>') {
|
if (url === '<auto>') {
|
||||||
|
if (settings.code != "") { // <auto> means no preload URL if code is set
|
||||||
|
settings.preload = settings.preload.slice(0, i).concat(settings.preload.slice(i + 1));
|
||||||
|
continue; // Skip this preload URL
|
||||||
|
}
|
||||||
const possibleBackend = new URL("./?api_updates=true", window.location.href)
|
const possibleBackend = new URL("./?api_updates=true", window.location.href)
|
||||||
await fetch(possibleBackend, {method: "HEAD"}).then((response) => {
|
await fetch(possibleBackend, {method: "HEAD"}).then((response) => {
|
||||||
if (response.ok && response.headers.get("Content-Type") === "text/event-stream") {
|
if (response.ok && response.headers.get("Content-Type") === "text/event-stream") {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import Model from "./Model.vue";
|
|||||||
import {inject, ref, type Ref} from "vue";
|
import {inject, ref, type Ref} from "vue";
|
||||||
|
|
||||||
const props = defineProps<{ viewer: InstanceType<typeof ModelViewerWrapper> | null }>();
|
const props = defineProps<{ viewer: InstanceType<typeof ModelViewerWrapper> | null }>();
|
||||||
const emit = defineEmits<{ remove: [string] }>()
|
const emit = defineEmits<{ removeModel: [string] }>()
|
||||||
|
|
||||||
let {sceneDocument} = inject<{ sceneDocument: Ref<Document> }>('sceneDocument')!!;
|
let {sceneDocument} = inject<{ sceneDocument: Ref<Document> }>('sceneDocument')!!;
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ function meshName(mesh: Mesh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onRemove(mesh: Mesh) {
|
function onRemove(mesh: Mesh) {
|
||||||
emit('remove', meshName(mesh))
|
emit('removeModel', meshName(mesh))
|
||||||
}
|
}
|
||||||
|
|
||||||
function findModel(name: string) {
|
function findModel(name: string) {
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {setupMonaco} from "./monaco.ts";
|
import {setupMonaco} from "./monaco.ts";
|
||||||
import {VueMonacoEditor} from '@guolao/vue-monaco-editor'
|
import {VueMonacoEditor} from '@guolao/vue-monaco-editor'
|
||||||
import {ref, shallowRef} from "vue";
|
import {nextTick, ref, shallowRef} from "vue";
|
||||||
import Loading from "../misc/Loading.vue";
|
import Loading from "../misc/Loading.vue";
|
||||||
import {asyncRun, pyodideWorker, resetState} from "./pyodide-worker-api.ts";
|
import {newPyodideWorker} from "./pyodide-worker-api.ts";
|
||||||
import {mdiCircleOpacity, mdiClose, mdiLockReset, mdiPlay, mdiReload, mdiRun} from "@mdi/js";
|
import {mdiCircleOpacity, mdiClose, mdiPlay, mdiReload, mdiShare} from "@mdi/js";
|
||||||
import {VBtn, VCard, VCardText, VSlider, VSpacer, VToolbar, VToolbarTitle} from "vuetify/components";
|
import {VBtn, VCard, VCardText, VSlider, VSpacer, VToolbar, VToolbarTitle} from "vuetify/components";
|
||||||
import SvgIcon from '@jamescoyle/vue-icon';
|
import SvgIcon from '@jamescoyle/vue-icon';
|
||||||
|
import {version as pyodideVersion} from "pyodide";
|
||||||
|
import {gzip} from 'pako';
|
||||||
|
import {b66Encode} from "./b66.ts";
|
||||||
|
import {Base64} from 'js-base64'; // More compatible with binary data from python...
|
||||||
|
import {NetworkUpdateEvent, NetworkUpdateEventModel} from "../misc/network.ts";
|
||||||
|
|
||||||
const props = defineProps<{ initialCode: string }>();
|
const props = defineProps<{ initialCode: string }>();
|
||||||
const emit = defineEmits<{ (e: 'close'): void }>()
|
const emit = defineEmits<{ close: [], updateModel: [NetworkUpdateEvent] }>()
|
||||||
|
|
||||||
// ============ LOAD MONACO EDITOR ============
|
// ============ LOAD MONACO EDITOR ============
|
||||||
setupMonaco() // Must be called before using the editor
|
setupMonaco() // Must be called before using the editor
|
||||||
@@ -20,12 +25,12 @@ const outputText = ref(``);
|
|||||||
function output(text: string) {
|
function output(text: string) {
|
||||||
outputText.value += text; // Append to output
|
outputText.value += text; // Append to output
|
||||||
console.log(text); // Also log to console
|
console.log(text); // Also log to console
|
||||||
setTimeout(() => { // Scroll to bottom? TODO: Test
|
nextTick(() => { // Scroll to bottom
|
||||||
const consoleElement = document.querySelector('.playground-console pre');
|
const consoleElement = document.querySelector('.playground-console');
|
||||||
if (consoleElement) {
|
if (consoleElement) {
|
||||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||||
}
|
}
|
||||||
}, 0);
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const MONACO_EDITOR_OPTIONS = {
|
const MONACO_EDITOR_OPTIONS = {
|
||||||
@@ -40,45 +45,120 @@ const handleMount = (editorInstance: typeof VueMonacoEditor) => (editor.value =
|
|||||||
const opacity = ref(0.9); // Opacity for the editor
|
const opacity = ref(0.9); // Opacity for the editor
|
||||||
|
|
||||||
// ============ LOAD PYODIDE (ASYNC) ============
|
// ============ LOAD PYODIDE (ASYNC) ============
|
||||||
|
let pyodideWorker: ReturnType<typeof newPyodideWorker> | null = (import.meta as any).hot?.data?.pyodideWorker || null;
|
||||||
const running = ref(true);
|
const running = ref(true);
|
||||||
|
|
||||||
async function setupPyodide() {
|
async function setupPyodide() {
|
||||||
|
running.value = true;
|
||||||
if (opacity.value == 0.0) opacity.value = 0.9; // User doesn't know how to show code again, reset after reopening
|
if (opacity.value == 0.0) opacity.value = 0.9; // User doesn't know how to show code again, reset after reopening
|
||||||
let firstTime = pyodideWorker === null;
|
if (pyodideWorker === null) {
|
||||||
if (firstTime) {
|
output("Creating new Pyodide worker...\n");
|
||||||
resetState();
|
pyodideWorker = newPyodideWorker({
|
||||||
output("Loading packages...\n");
|
indexURL: `https://cdn.jsdelivr.net/pyodide/v${pyodideVersion}/full/`, // FIXME: Local deployment?
|
||||||
await asyncRun(`import micropip, asyncio
|
packages: ["micropip", "sqlite3"], // Faster load if done here
|
||||||
micropip.set_index_urls(["https://yeicor.github.io/OCP.wasm", "https://pypi.org/simple"])
|
});
|
||||||
await (micropip.install("lib3mf"))
|
if ((import.meta as any).hot) (import.meta as any).hot.data.pyodideWorker = pyodideWorker
|
||||||
micropip.add_mock_package("py-lib3mf", "2.4.1", modules={"py_lib3mf": '''from lib3mf import *'''})
|
|
||||||
await (micropip.install(["build123d"]))`, output);
|
|
||||||
if (props.initialCode != "") {
|
|
||||||
await runCode();
|
|
||||||
opacity.value = 0.0; // Hide editor after running initial code
|
|
||||||
} else {
|
|
||||||
output("Ready for custom code.\n");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
output("Reusing existing Pyodide instance...\n");
|
output("Reusing existing Pyodide instance...\n");
|
||||||
}
|
}
|
||||||
|
output("Preloading packages...\n");
|
||||||
|
await pyodideWorker.asyncRun(`import micropip, asyncio
|
||||||
|
micropip.set_index_urls(["https://yeicor.github.io/OCP.wasm", "https://pypi.org/simple"])
|
||||||
|
await (micropip.install("lib3mf"))
|
||||||
|
micropip.add_mock_package("py-lib3mf", "2.4.1", modules={"py_lib3mf": '''from lib3mf import *'''})
|
||||||
|
await (micropip.install(["https://files.pythonhosted.org/packages/67/25/80be117f39ff5652a4fdbd761b061123c5425e379f4b0a5ece4353215d86/yacv_server-0.10.0a4-py3-none-any.whl"]))
|
||||||
|
from yacv_server import *`, output, output); // Also import yacv_server here for faster custom code execution
|
||||||
running.value = false; // Indicate that Pyodide is ready
|
running.value = false; // Indicate that Pyodide is ready
|
||||||
|
output("Pyodide worker initialized.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
setupPyodide()
|
|
||||||
|
|
||||||
async function runCode() {
|
async function runCode() {
|
||||||
|
if (pyodideWorker === null) {
|
||||||
|
output("Pyodide worker is not initialized. Please wait...\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (running.value) {
|
||||||
|
output("Pyodide is already running. Please wait...\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
output("Running code...\n");
|
output("Running code...\n");
|
||||||
try {
|
try {
|
||||||
running.value = true;
|
running.value = true;
|
||||||
await asyncRun(code.value, output);
|
await pyodideWorker.asyncRun(code.value, output, (msg: string) => {
|
||||||
|
// Detect models printed to console (since http server is not available in pyodide)
|
||||||
|
if (msg.startsWith(yacvServerModelPrefix)) {
|
||||||
|
const modelData = msg.slice(yacvServerModelPrefix.length);
|
||||||
|
onModelData(modelData);
|
||||||
|
} else {
|
||||||
|
output(msg); // Print other messages directly
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
output(`Error running initial code: ${e}\n`);
|
output(`Error running initial code: ${e}\n`);
|
||||||
} finally {
|
} finally {
|
||||||
running.value = false; // Indicate that Pyodide is ready
|
running.value = false; // Indicate that Pyodide is ready
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const yacvServerModelPrefix = "yacv_server://model/";
|
||||||
|
|
||||||
|
function onModelData(modelData: string) {
|
||||||
|
output(`Model data detected... ${modelData.length}B\n`);
|
||||||
|
// Decode the model data and emit the event for the interface to handle
|
||||||
|
// - Start by finding the end of the initial json object by looking for brackets.
|
||||||
|
let i = 0;
|
||||||
|
let openBrackets = 0;
|
||||||
|
for (; i < modelData.length; i++) {
|
||||||
|
if (modelData[i] === '{') openBrackets++;
|
||||||
|
else if (modelData[i] === '}') openBrackets--;
|
||||||
|
if (openBrackets === 0) break; // Found the end of the JSON object
|
||||||
|
}
|
||||||
|
if (openBrackets !== 0) throw `Error: Invalid model data received: ${modelData}\n`
|
||||||
|
const jsonData = modelData.slice(0, i + 1); // Extract the JSON part and parse it into the proper class
|
||||||
|
let modelMetadataRaw = JSON.parse(jsonData);
|
||||||
|
const modelMetadata: any = new NetworkUpdateEventModel(modelMetadataRaw.name, "Wait for it...", modelMetadataRaw.hash, modelMetadataRaw.is_remove)
|
||||||
|
// console.debug(`Model metadata:`, modelMetadata);
|
||||||
|
output(`Model metadata: ${JSON.stringify(modelMetadata)}\n`);
|
||||||
|
// - Now decode the rest of the model data which is a single base64 encoded glb file (or an empty string)
|
||||||
|
if (!modelMetadata.isRemove) {
|
||||||
|
const binaryData = Base64.toUint8Array(modelData.slice(i + 1)); // Extract the base64 part
|
||||||
|
console.assert(binaryData.slice(0, 4).toString() == "103,108,84,70", // Ugly...
|
||||||
|
"Invalid GLTF binary data received: " + binaryData.slice(0, 4).toString());
|
||||||
|
// - Create a Blob from the binary data to be used as a URL
|
||||||
|
const blob = new Blob([binaryData], {type: 'model/gltf-binary'});
|
||||||
|
modelMetadata.url = URL.createObjectURL(blob); // Set the hacked URL in the model metadata
|
||||||
|
}
|
||||||
|
// - Emit the event with the model metadata and URL
|
||||||
|
let networkUpdateEvent = new NetworkUpdateEvent([modelMetadata], () => {
|
||||||
|
});
|
||||||
|
emit('updateModel', networkUpdateEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetWorker() {
|
||||||
|
code.value = props.initialCode; // Reset code to initial state
|
||||||
|
if (pyodideWorker) {
|
||||||
|
pyodideWorker.terminate(); // Terminate existing worker
|
||||||
|
pyodideWorker = null; // Reset worker reference
|
||||||
|
}
|
||||||
|
outputText.value = ``; // Clear output text
|
||||||
|
setupPyodide(); // Reinitialize Pyodide
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareLink() {
|
||||||
|
const baseUrl = window.location
|
||||||
|
const urlParams = new URLSearchParams(baseUrl.search); // Keep all previous URL parameters
|
||||||
|
urlParams.set('code', b66Encode(gzip(code.value, {level: 9}))); // Compress and encode the code
|
||||||
|
const shareUrl = `${baseUrl.origin}${baseUrl.pathname}?${urlParams.toString()}`;
|
||||||
|
output(`Share link ready: ${shareUrl}\n`)
|
||||||
|
navigator.clipboard.writeText(shareUrl)
|
||||||
|
.then(() => output("Link copied to clipboard!\n"))
|
||||||
|
.catch(err => output(`Failed to copy link: ${err}\n`));
|
||||||
|
}
|
||||||
|
|
||||||
|
const reused = (import.meta as any).hot?.data?.pyodideWorker !== undefined;
|
||||||
|
setupPyodide().then(() => {
|
||||||
|
if (props.initialCode != "" && !reused) runCode();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -94,7 +174,7 @@ async function runCode() {
|
|||||||
|
|
||||||
<!-- TODO: snapshots... -->
|
<!-- TODO: snapshots... -->
|
||||||
|
|
||||||
<v-btn icon @click="resetState()">
|
<v-btn icon @click="resetWorker()">
|
||||||
<svg-icon :path="mdiReload" type="mdi"/>
|
<svg-icon :path="mdiReload" type="mdi"/>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
@@ -102,6 +182,10 @@ async function runCode() {
|
|||||||
<svg-icon :path="mdiPlay" type="mdi"/>
|
<svg-icon :path="mdiPlay" type="mdi"/>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
|
<v-btn icon @click="shareLink()">
|
||||||
|
<svg-icon :path="mdiShare" type="mdi"/>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
<v-btn icon @click="emit('close')">
|
<v-btn icon @click="emit('close')">
|
||||||
<svg-icon :path="mdiClose" type="mdi"/>
|
<svg-icon :path="mdiClose" type="mdi"/>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@@ -110,13 +194,8 @@ async function runCode() {
|
|||||||
<!-- Only show content if opacity is greater than 0 -->
|
<!-- Only show content if opacity is greater than 0 -->
|
||||||
<div class="playground-container">
|
<div class="playground-container">
|
||||||
<div class="playground-editor">
|
<div class="playground-editor">
|
||||||
<VueMonacoEditor
|
<VueMonacoEditor v-model:value="code" :theme="editorTheme" :options="MONACO_EDITOR_OPTIONS"
|
||||||
v-model:value="code"
|
language="python" @mount="handleMount"/>
|
||||||
:theme="editorTheme"
|
|
||||||
:options="MONACO_EDITOR_OPTIONS"
|
|
||||||
language="python"
|
|
||||||
@mount="handleMount"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="playground-console">
|
<div class="playground-console">
|
||||||
<h3>Console Output</h3>
|
<h3>Console Output</h3>
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import type ModelViewerWrapper from "../viewer/ModelViewerWrapper.vue";
|
|||||||
import {defineAsyncComponent, ref, type Ref} from "vue";
|
import {defineAsyncComponent, ref, type Ref} from "vue";
|
||||||
import type {SelectionInfo} from "./selection";
|
import type {SelectionInfo} from "./selection";
|
||||||
import {settings} from "../misc/settings.ts";
|
import {settings} from "../misc/settings.ts";
|
||||||
|
import type {NetworkUpdateEvent} from "../misc/network.ts";
|
||||||
|
import IfNotSmallBuild from "../misc/IfNotSmallBuild.vue";
|
||||||
|
|
||||||
const SelectionComponent = defineAsyncComponent({
|
const SelectionComponent = defineAsyncComponent({
|
||||||
loader: () => import("./Selection.vue"),
|
loader: () => import("./Selection.vue"),
|
||||||
@@ -43,7 +45,7 @@ const PlaygroundDialogContent = defineAsyncComponent({
|
|||||||
|
|
||||||
|
|
||||||
let props = defineProps<{ viewer: InstanceType<typeof ModelViewerWrapper> | null }>();
|
let props = defineProps<{ viewer: InstanceType<typeof ModelViewerWrapper> | null }>();
|
||||||
const emit = defineEmits<{ findModel: [string] }>()
|
const emit = defineEmits<{ findModel: [string], updateModel: [NetworkUpdateEvent] }>()
|
||||||
|
|
||||||
const sett = ref<any | null>(null);
|
const sett = ref<any | null>(null);
|
||||||
const showPlaygroundDialog = ref(false);
|
const showPlaygroundDialog = ref(false);
|
||||||
@@ -162,14 +164,17 @@ window.addEventListener('keydown', (event) => {
|
|||||||
<h5>Extras</h5>
|
<h5>Extras</h5>
|
||||||
<v-dialog v-model="showPlaygroundDialog" persistent :scrim="false" attach="body">
|
<v-dialog v-model="showPlaygroundDialog" persistent :scrim="false" attach="body">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn icon v-bind="props" style="width: 100%">
|
<v-btn v-bind="props" style="width: 100%">
|
||||||
<v-tooltip activator="parent">Open a python editor and build models directly in the browser!</v-tooltip>
|
<v-tooltip activator="parent">Open a python editor and build models directly in the browser!</v-tooltip>
|
||||||
<svg-icon :path="mdiScriptTextPlay" type="mdi"/>
|
<svg-icon :path="mdiScriptTextPlay" type="mdi"/>
|
||||||
Sandbox
|
Sandbox
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:default="{ isActive }">
|
<template v-slot:default="{ isActive }">
|
||||||
<playground-dialog-content v-if="sett != null" :initial-code="sett.value.code" @close="isActive.value = false"/>
|
<if-not-small-build>
|
||||||
|
<playground-dialog-content v-if="sett != null" :initial-code="sett.code" @close="isActive.value = false"
|
||||||
|
@update-model="(event: NetworkUpdateEvent) => emit('updateModel', event)"/>
|
||||||
|
</if-not-small-build>
|
||||||
</template>
|
</template>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
<v-btn icon @click="downloadSceneGlb">
|
<v-btn icon @click="downloadSceneGlb">
|
||||||
|
|||||||
54
frontend/tools/b66.ts
Normal file
54
frontend/tools/b66.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// B66 encoding and decoding functions for compact url query parameter values. https://gist.github.com/danneu/6755394
|
||||||
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_~";
|
||||||
|
|
||||||
|
export function b66Encode(data: Uint8Array): string {
|
||||||
|
let result = "";
|
||||||
|
let bits = 0;
|
||||||
|
let value = 0;
|
||||||
|
|
||||||
|
for (let byte of data) {
|
||||||
|
value = (value << 8) | byte;
|
||||||
|
bits += 8;
|
||||||
|
|
||||||
|
while (bits >= 6) {
|
||||||
|
bits -= 6;
|
||||||
|
result += alphabet[(value >> bits) & 0x3F];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits > 0) {
|
||||||
|
result += alphabet[(value << (6 - bits)) & 0x3F];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function b66Decode(encoded: string): Uint8Array {
|
||||||
|
let result = [];
|
||||||
|
let bits = 0;
|
||||||
|
let value = 0;
|
||||||
|
|
||||||
|
for (let char of encoded) {
|
||||||
|
const index = alphabet.indexOf(char);
|
||||||
|
if (index === -1) {
|
||||||
|
throw new Error(`Invalid character '${char}' in B66 encoded string.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = (value << 6) | index;
|
||||||
|
bits += 6;
|
||||||
|
|
||||||
|
while (bits >= 8) {
|
||||||
|
bits -= 8;
|
||||||
|
result.push((value >> bits) & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits > 0) {
|
||||||
|
// If there are leftover bits, they should not be present in a valid B66 encoding.
|
||||||
|
if (value << (8 - bits)) {
|
||||||
|
throw new Error("Invalid B66 encoding: leftover bits.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Uint8Array(result);
|
||||||
|
}
|
||||||
@@ -1,44 +1,31 @@
|
|||||||
// Each message needs a unique id to identify the response. In a real example,
|
import type {loadPyodide} from "pyodide";
|
||||||
// we might use a real uuid package
|
|
||||||
let lastId = 1;
|
|
||||||
|
|
||||||
function getId() {
|
/** Simple API for the Pyodide worker. */
|
||||||
return lastId++;
|
export function newPyodideWorker(initOpts: Parameters<typeof loadPyodide>[0]) {
|
||||||
|
let worker = new Worker(new URL('./pyodide-worker.ts', import.meta.url), {type: "module"});
|
||||||
|
worker.postMessage(initOpts);
|
||||||
|
return {
|
||||||
|
asyncRun: (code: String, stdout: (msg: string) => void, stderr: (msg: string) => void) => new Promise((resolve, reject) => {
|
||||||
|
worker.addEventListener("message", function listener(event) {
|
||||||
|
if (event.data?.stdout) {
|
||||||
|
stdout(event.data.stdout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.data?.stderr) {
|
||||||
|
stderr(event.data.stderr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Result or error.
|
||||||
|
worker.removeEventListener("message", listener);
|
||||||
|
if (event.data?.error) {
|
||||||
|
reject(event.data.error);
|
||||||
|
} else {
|
||||||
|
resolve(event.data?.result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
worker.postMessage(code);
|
||||||
|
}),
|
||||||
|
terminate: () => worker.terminate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an id to msg, send it to worker, then wait for a response with the same id.
|
|
||||||
// When we get such a response, use it to resolve the promise.
|
|
||||||
function requestResponse(worker: Worker, code: String, output: (msg: string) => void) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const idWorker = getId();
|
|
||||||
worker.addEventListener("message", function listener(event) {
|
|
||||||
if (event.data?.stdout) {
|
|
||||||
output(event.data.stdout + "\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.data?.stderr) {
|
|
||||||
output(event.data.stderr + "\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.data?.id !== idWorker) return;
|
|
||||||
// This listener is done so remove it.
|
|
||||||
worker.removeEventListener("message", listener);
|
|
||||||
// Filter the id out of the result
|
|
||||||
const {id, ...rest} = event.data;
|
|
||||||
resolve(rest);
|
|
||||||
});
|
|
||||||
worker.postMessage({id: idWorker, code});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function asyncRun(code: String, output: (msg: string) => void) {
|
|
||||||
return requestResponse(pyodideWorker as Worker, code, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetState() {
|
|
||||||
// Reset the worker state by terminating it and creating a new one.
|
|
||||||
if (pyodideWorker) pyodideWorker.terminate();
|
|
||||||
pyodideWorker = new Worker(new URL('./pyodide-worker.ts', import.meta.url), {type: "module"});
|
|
||||||
}
|
|
||||||
|
|
||||||
export let pyodideWorker: Worker | null = null;
|
|
||||||
@@ -1,27 +1,33 @@
|
|||||||
import {loadPyodide, version} from "pyodide";
|
import {loadPyodide, type PyodideInterface} from "pyodide";
|
||||||
|
|
||||||
let pyodideReadyPromise = loadPyodide({
|
let myLoadPyodide = (initOpts: Parameters<typeof loadPyodide>[0]) => loadPyodide({
|
||||||
indexURL: `https://cdn.jsdelivr.net/pyodide/v${version}/full/`, // FIXME: Local deployment?
|
...initOpts,
|
||||||
packages: ["micropip", "sqlite3"], // Preloaded faster here...
|
stdout: (msg) => self.postMessage({stdout: msg + "\n"}), // Add newline for better readability
|
||||||
stdout: (msg) => self.postMessage({stdout: msg}),
|
stderr: (msg) => self.postMessage({stderr: msg + "\n"}), // Add newline for better readability
|
||||||
stderr: (msg) => self.postMessage({stderr: msg}),
|
|
||||||
stdin: () => {
|
stdin: () => {
|
||||||
console.warn("Input requested by Python code, but stdin is not supported in this playground.");
|
console.warn("Input requested by Python code, but stdin is not supported in this playground.");
|
||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
self.onmessage = async (event) => {
|
let pyodideReadyPromise: Promise<PyodideInterface> | null = null;
|
||||||
|
|
||||||
|
self.onmessage = async (event: MessageEvent<any>) => {
|
||||||
|
if (!pyodideReadyPromise) { // First message is always the init message
|
||||||
|
// If we haven't loaded Pyodide yet, do so now.
|
||||||
|
// This is a singleton, so we only load it once.
|
||||||
|
pyodideReadyPromise = myLoadPyodide(event.data as Parameters<typeof loadPyodide>[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// All other messages are code to run.
|
||||||
|
let code = event.data as string;
|
||||||
// make sure loading is done
|
// make sure loading is done
|
||||||
const pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
const {id, code} = event.data;
|
|
||||||
// Now load any packages we need, run the code, and send the result back.
|
// Now load any packages we need, run the code, and send the result back.
|
||||||
await pyodide.loadPackagesFromImports(code);
|
await pyodide.loadPackagesFromImports(code);
|
||||||
try {
|
try {
|
||||||
// Execute the python code in this context
|
self.postMessage({result: await pyodide.runPythonAsync(code)});
|
||||||
const result = await pyodide.runPythonAsync(code);
|
|
||||||
self.postMessage({result, id});
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
self.postMessage({error: error.message, id});
|
self.postMessage({error: error.message});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -23,7 +23,9 @@
|
|||||||
"@jamescoyle/vue-icon": "^0.1.2",
|
"@jamescoyle/vue-icon": "^0.1.2",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@mdi/svg": "^7.4.47",
|
"@mdi/svg": "^7.4.47",
|
||||||
|
"js-base64": "^3.7.7",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
|
"pako": "^2.1.0",
|
||||||
"pyodide": "^0.28.0",
|
"pyodide": "^0.28.0",
|
||||||
"three": "^0.178.0",
|
"three": "^0.178.0",
|
||||||
"three-mesh-bvh": "^0.9.0",
|
"three-mesh-bvh": "^0.9.0",
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/node20": "^20.1.4",
|
"@tsconfig/node20": "^20.1.4",
|
||||||
"@types/node": "^22.9.3",
|
"@types/node": "^22.9.3",
|
||||||
|
"@types/pako": "^2.0.3",
|
||||||
"@types/three": "^0.178.0",
|
"@types/three": "^0.178.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.0.0",
|
"@vitejs/plugin-vue-jsx": "^5.0.0",
|
||||||
@@ -45,7 +48,7 @@
|
|||||||
"terser": "^5.36.0",
|
"terser": "^5.36.0",
|
||||||
"typescript": "~5.8.0",
|
"typescript": "~5.8.0",
|
||||||
"vite": "^7.0.0",
|
"vite": "^7.0.0",
|
||||||
"vue-tsc": "^3.0.0",
|
"vite-plugin-static-copy": "^3.1.1",
|
||||||
"vite-plugin-static-copy": "^3.1.1"
|
"vue-tsc": "^3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
194
poetry.lock
generated
194
poetry.lock
generated
@@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anytree"
|
name = "anytree"
|
||||||
@@ -6,6 +6,7 @@ version = "2.13.0"
|
|||||||
description = "Powerful and Lightweight Python Tree Data Structure with various plugins"
|
description = "Powerful and Lightweight Python Tree Data Structure with various plugins"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.9.2"
|
python-versions = "<4.0,>=3.9.2"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "anytree-2.13.0-py3-none-any.whl", hash = "sha256:4cbcf10df36b1f1cba131b7e487ff3edafc9d6e932a3c70071b5b768bab901ff"},
|
{file = "anytree-2.13.0-py3-none-any.whl", hash = "sha256:4cbcf10df36b1f1cba131b7e487ff3edafc9d6e932a3c70071b5b768bab901ff"},
|
||||||
{file = "anytree-2.13.0.tar.gz", hash = "sha256:c9d3aa6825fdd06af7ebb05b4ef291d2db63e62bb1f9b7d9b71354be9d362714"},
|
{file = "anytree-2.13.0.tar.gz", hash = "sha256:c9d3aa6825fdd06af7ebb05b4ef291d2db63e62bb1f9b7d9b71354be9d362714"},
|
||||||
@@ -17,6 +18,7 @@ version = "3.0.0"
|
|||||||
description = "Annotate AST trees with source code positions"
|
description = "Annotate AST trees with source code positions"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
|
{file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
|
||||||
{file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
|
{file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
|
||||||
@@ -32,6 +34,7 @@ version = "0.9.1"
|
|||||||
description = "A python CAD programming library"
|
description = "A python CAD programming library"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<3.14,>=3.10"
|
python-versions = "<3.14,>=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "build123d-0.9.1-py3-none-any.whl", hash = "sha256:0e2a3c171d7db55329201438a95ab888df6c1b0fd13067a3780efcc8fbe1cc78"},
|
{file = "build123d-0.9.1-py3-none-any.whl", hash = "sha256:0e2a3c171d7db55329201438a95ab888df6c1b0fd13067a3780efcc8fbe1cc78"},
|
||||||
{file = "build123d-0.9.1.tar.gz", hash = "sha256:8bc179cb65c7e7393191ad7113f13781fabf75892be07060f2bebf95369c093b"},
|
{file = "build123d-0.9.1.tar.gz", hash = "sha256:8bc179cb65c7e7393191ad7113f13781fabf75892be07060f2bebf95369c093b"},
|
||||||
@@ -63,6 +66,7 @@ version = "7.8.1.1.post1"
|
|||||||
description = "Python wrapper for Open CASCADE Technology 3D geometry library based on the official CadQuery/OCP sources"
|
description = "Python wrapper for Open CASCADE Technology 3D geometry library based on the official CadQuery/OCP sources"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<3.14,>=3.10"
|
python-versions = "<3.14,>=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "cadquery_ocp-7.8.1.1.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4413961e98a90686a56c2ac58b126773c7da4eb82b967ddcc1f394fe6a7b71ad"},
|
{file = "cadquery_ocp-7.8.1.1.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4413961e98a90686a56c2ac58b126773c7da4eb82b967ddcc1f394fe6a7b71ad"},
|
||||||
{file = "cadquery_ocp-7.8.1.1.post1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:687c8a22d6248b28e44f136cecf93d274a897efbd0d8b36e38d4e207fecf1d84"},
|
{file = "cadquery_ocp-7.8.1.1.post1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:687c8a22d6248b28e44f136cecf93d274a897efbd0d8b36e38d4e207fecf1d84"},
|
||||||
@@ -91,10 +95,12 @@ version = "0.4.6"
|
|||||||
description = "Cross-platform colored terminal text."
|
description = "Cross-platform colored terminal text."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
|
groups = ["main", "dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||||
]
|
]
|
||||||
|
markers = {main = "sys_platform == \"win32\""}
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "contourpy"
|
name = "contourpy"
|
||||||
@@ -102,6 +108,7 @@ version = "1.3.2"
|
|||||||
description = "Python library for calculating contours of 2D quadrilateral grids"
|
description = "Python library for calculating contours of 2D quadrilateral grids"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934"},
|
{file = "contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934"},
|
||||||
{file = "contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989"},
|
{file = "contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989"},
|
||||||
@@ -178,6 +185,7 @@ version = "0.12.1"
|
|||||||
description = "Composable style cycles"
|
description = "Composable style cycles"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
|
{file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
|
||||||
{file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
|
{file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
|
||||||
@@ -193,6 +201,7 @@ version = "0.6.7"
|
|||||||
description = "Easily serialize dataclasses to and from JSON."
|
description = "Easily serialize dataclasses to and from JSON."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4.0,>=3.7"
|
python-versions = "<4.0,>=3.7"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"},
|
{file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"},
|
||||||
{file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"},
|
{file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"},
|
||||||
@@ -208,6 +217,7 @@ version = "5.2.1"
|
|||||||
description = "Decorators for Humans"
|
description = "Decorators for Humans"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"},
|
{file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"},
|
||||||
{file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"},
|
{file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"},
|
||||||
@@ -219,6 +229,7 @@ version = "1.2.18"
|
|||||||
description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
|
description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"},
|
{file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"},
|
||||||
{file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"},
|
{file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"},
|
||||||
@@ -228,7 +239,7 @@ files = [
|
|||||||
wrapt = ">=1.10,<2"
|
wrapt = ">=1.10,<2"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"]
|
dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "exceptiongroup"
|
name = "exceptiongroup"
|
||||||
@@ -236,6 +247,8 @@ version = "1.3.0"
|
|||||||
description = "Backport of PEP 654 (exception groups)"
|
description = "Backport of PEP 654 (exception groups)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
groups = ["main"]
|
||||||
|
markers = "python_version == \"3.10\""
|
||||||
files = [
|
files = [
|
||||||
{file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
|
{file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
|
||||||
{file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
|
{file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
|
||||||
@@ -253,13 +266,14 @@ version = "2.2.0"
|
|||||||
description = "Get the currently executing AST node of a frame, and other information"
|
description = "Get the currently executing AST node of a frame, and other information"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"},
|
{file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"},
|
||||||
{file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"},
|
{file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
|
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ezdxf"
|
name = "ezdxf"
|
||||||
@@ -267,6 +281,7 @@ version = "1.4.2"
|
|||||||
description = "A Python package to create/manipulate DXF drawings."
|
description = "A Python package to create/manipulate DXF drawings."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "ezdxf-1.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:94e62d4da459d86e6d1630c09109b7b6fd9672f7ff8fab4eb9d3c91f75e0a18e"},
|
{file = "ezdxf-1.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:94e62d4da459d86e6d1630c09109b7b6fd9672f7ff8fab4eb9d3c91f75e0a18e"},
|
||||||
{file = "ezdxf-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:63aae1eb68aa30e1bbee38886e5b8bf602c9a4a0b672f0b2b31c5cdb29d6c533"},
|
{file = "ezdxf-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:63aae1eb68aa30e1bbee38886e5b8bf602c9a4a0b672f0b2b31c5cdb29d6c533"},
|
||||||
@@ -330,6 +345,7 @@ version = "4.58.5"
|
|||||||
description = "Tools to manipulate font files"
|
description = "Tools to manipulate font files"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "fonttools-4.58.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d500d399aa4e92d969a0d21052696fa762385bb23c3e733703af4a195ad9f34c"},
|
{file = "fonttools-4.58.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d500d399aa4e92d969a0d21052696fa762385bb23c3e733703af4a195ad9f34c"},
|
||||||
{file = "fonttools-4.58.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b00530b84f87792891874938bd42f47af2f7f4c2a1d70466e6eb7166577853ab"},
|
{file = "fonttools-4.58.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b00530b84f87792891874938bd42f47af2f7f4c2a1d70466e6eb7166577853ab"},
|
||||||
@@ -376,18 +392,18 @@ files = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"]
|
all = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\"", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0) ; python_version <= \"3.12\"", "xattr ; sys_platform == \"darwin\"", "zopfli (>=0.1.4)"]
|
||||||
graphite = ["lz4 (>=1.7.4.2)"]
|
graphite = ["lz4 (>=1.7.4.2)"]
|
||||||
interpolatable = ["munkres", "pycairo", "scipy"]
|
interpolatable = ["munkres ; platform_python_implementation == \"PyPy\"", "pycairo", "scipy ; platform_python_implementation != \"PyPy\""]
|
||||||
lxml = ["lxml (>=4.0)"]
|
lxml = ["lxml (>=4.0)"]
|
||||||
pathops = ["skia-pathops (>=0.5.0)"]
|
pathops = ["skia-pathops (>=0.5.0)"]
|
||||||
plot = ["matplotlib"]
|
plot = ["matplotlib"]
|
||||||
repacker = ["uharfbuzz (>=0.23.0)"]
|
repacker = ["uharfbuzz (>=0.23.0)"]
|
||||||
symfont = ["sympy"]
|
symfont = ["sympy"]
|
||||||
type1 = ["xattr"]
|
type1 = ["xattr ; sys_platform == \"darwin\""]
|
||||||
ufo = ["fs (>=2.2.0,<3)"]
|
ufo = ["fs (>=2.2.0,<3)"]
|
||||||
unicode = ["unicodedata2 (>=15.1.0)"]
|
unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""]
|
||||||
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
|
woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipython"
|
name = "ipython"
|
||||||
@@ -395,6 +411,7 @@ version = "8.37.0"
|
|||||||
description = "IPython: Productive Interactive Computing"
|
description = "IPython: Productive Interactive Computing"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"},
|
{file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"},
|
||||||
{file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"},
|
{file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"},
|
||||||
@@ -416,7 +433,7 @@ typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""}
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
|
all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
|
||||||
black = ["black"]
|
black = ["black"]
|
||||||
doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing_extensions"]
|
doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"]
|
||||||
kernel = ["ipykernel"]
|
kernel = ["ipykernel"]
|
||||||
matplotlib = ["matplotlib"]
|
matplotlib = ["matplotlib"]
|
||||||
nbconvert = ["nbconvert"]
|
nbconvert = ["nbconvert"]
|
||||||
@@ -433,6 +450,7 @@ version = "0.19.2"
|
|||||||
description = "An autocompletion tool for Python that can be used for text editors."
|
description = "An autocompletion tool for Python that can be used for text editors."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
|
{file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
|
||||||
{file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
|
{file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
|
||||||
@@ -452,6 +470,7 @@ version = "1.4.8"
|
|||||||
description = "A fast implementation of the Cassowary constraint solver"
|
description = "A fast implementation of the Cassowary constraint solver"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"},
|
{file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"},
|
||||||
{file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"},
|
{file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"},
|
||||||
@@ -541,6 +560,7 @@ version = "3.26.1"
|
|||||||
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"},
|
{file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"},
|
||||||
{file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"},
|
{file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"},
|
||||||
@@ -560,6 +580,7 @@ version = "3.10.3"
|
|||||||
description = "Python plotting package"
|
description = "Python plotting package"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "matplotlib-3.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:213fadd6348d106ca7db99e113f1bea1e65e383c3ba76e8556ba4a3054b65ae7"},
|
{file = "matplotlib-3.10.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:213fadd6348d106ca7db99e113f1bea1e65e383c3ba76e8556ba4a3054b65ae7"},
|
||||||
{file = "matplotlib-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3bec61cb8221f0ca6313889308326e7bb303d0d302c5cc9e523b2f2e6c73deb"},
|
{file = "matplotlib-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3bec61cb8221f0ca6313889308326e7bb303d0d302c5cc9e523b2f2e6c73deb"},
|
||||||
@@ -617,6 +638,7 @@ version = "0.1.7"
|
|||||||
description = "Inline Matplotlib backend for Jupyter"
|
description = "Inline Matplotlib backend for Jupyter"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
|
{file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
|
||||||
{file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
|
{file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
|
||||||
@@ -625,12 +647,26 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
traitlets = "*"
|
traitlets = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mslex"
|
||||||
|
version = "1.3.0"
|
||||||
|
description = "shlex for windows"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
groups = ["dev"]
|
||||||
|
markers = "sys_platform == \"win32\""
|
||||||
|
files = [
|
||||||
|
{file = "mslex-1.3.0-py3-none-any.whl", hash = "sha256:c7074b347201b3466fc077c5692fbce9b5f62a63a51f537a53fbbd02eff2eea4"},
|
||||||
|
{file = "mslex-1.3.0.tar.gz", hash = "sha256:641c887d1d3db610eee2af37a8e5abda3f70b3006cdfd2d0d29dc0d1ae28a85d"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy-extensions"
|
name = "mypy-extensions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
description = "Type system extensions for programs checked with the mypy type checker."
|
description = "Type system extensions for programs checked with the mypy type checker."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
|
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
|
||||||
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
|
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
|
||||||
@@ -642,6 +678,7 @@ version = "2.2.6"
|
|||||||
description = "Fundamental package for array computing in Python"
|
description = "Fundamental package for array computing in Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"},
|
{file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"},
|
||||||
{file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"},
|
{file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"},
|
||||||
@@ -706,6 +743,7 @@ version = "0.5.0"
|
|||||||
description = ""
|
description = ""
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "ocpsvg-0.5.0-py3-none-any.whl", hash = "sha256:68cafdc3d681a1707530360baf2d51cfd58414b7d439f42eafbd31e842cf295e"},
|
{file = "ocpsvg-0.5.0-py3-none-any.whl", hash = "sha256:68cafdc3d681a1707530360baf2d51cfd58414b7d439f42eafbd31e842cf295e"},
|
||||||
{file = "ocpsvg-0.5.0.tar.gz", hash = "sha256:5cd8dbec8bf590d373a82aaebeab241838185aab04ee2859f33b9d7956bbfba6"},
|
{file = "ocpsvg-0.5.0.tar.gz", hash = "sha256:5cd8dbec8bf590d373a82aaebeab241838185aab04ee2859f33b9d7956bbfba6"},
|
||||||
@@ -724,6 +762,7 @@ version = "25.0"
|
|||||||
description = "Core utilities for Python packages"
|
description = "Core utilities for Python packages"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
|
{file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
|
||||||
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
|
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
|
||||||
@@ -735,6 +774,7 @@ version = "0.8.4"
|
|||||||
description = "A Python Parser"
|
description = "A Python Parser"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
|
{file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
|
||||||
{file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
|
{file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
|
||||||
@@ -750,6 +790,8 @@ version = "4.9.0"
|
|||||||
description = "Pexpect allows easy control of interactive console applications."
|
description = "Pexpect allows easy control of interactive console applications."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
|
markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
|
||||||
files = [
|
files = [
|
||||||
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
|
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
|
||||||
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
|
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
|
||||||
@@ -764,6 +806,7 @@ version = "11.3.0"
|
|||||||
description = "Python Imaging Library (Fork)"
|
description = "Python Imaging Library (Fork)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"},
|
{file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"},
|
||||||
{file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"},
|
{file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"},
|
||||||
@@ -879,15 +922,28 @@ fpx = ["olefile"]
|
|||||||
mic = ["olefile"]
|
mic = ["olefile"]
|
||||||
test-arrow = ["pyarrow"]
|
test-arrow = ["pyarrow"]
|
||||||
tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"]
|
tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"]
|
||||||
typing = ["typing-extensions"]
|
typing = ["typing-extensions ; python_version < \"3.10\""]
|
||||||
xmp = ["defusedxml"]
|
xmp = ["defusedxml"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "poetry-core"
|
||||||
|
version = "2.1.3"
|
||||||
|
description = "Poetry PEP 517 Build Backend"
|
||||||
|
optional = false
|
||||||
|
python-versions = "<4.0,>=3.9"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "poetry_core-2.1.3-py3-none-any.whl", hash = "sha256:2c704f05016698a54ca1d327f46ce2426d72eaca6ff614132c8477c292266771"},
|
||||||
|
{file = "poetry_core-2.1.3.tar.gz", hash = "sha256:0522a015477ed622c89aad56a477a57813cace0c8e7ff2a2906b7ef4a2e296a4"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prompt-toolkit"
|
name = "prompt-toolkit"
|
||||||
version = "3.0.51"
|
version = "3.0.51"
|
||||||
description = "Library for building powerful interactive command lines in Python"
|
description = "Library for building powerful interactive command lines in Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"},
|
{file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"},
|
||||||
{file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"},
|
{file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"},
|
||||||
@@ -896,12 +952,45 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
wcwidth = "*"
|
wcwidth = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "psutil"
|
||||||
|
version = "6.1.1"
|
||||||
|
description = "Cross-platform lib for process and system monitoring in Python."
|
||||||
|
optional = false
|
||||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"},
|
||||||
|
{file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"},
|
||||||
|
{file = "psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4"},
|
||||||
|
{file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468"},
|
||||||
|
{file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca"},
|
||||||
|
{file = "psutil-6.1.1-cp27-none-win32.whl", hash = "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac"},
|
||||||
|
{file = "psutil-6.1.1-cp27-none-win_amd64.whl", hash = "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030"},
|
||||||
|
{file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"},
|
||||||
|
{file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"},
|
||||||
|
{file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"},
|
||||||
|
{file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"},
|
||||||
|
{file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"},
|
||||||
|
{file = "psutil-6.1.1-cp36-cp36m-win32.whl", hash = "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603"},
|
||||||
|
{file = "psutil-6.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303"},
|
||||||
|
{file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"},
|
||||||
|
{file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"},
|
||||||
|
{file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"]
|
||||||
|
test = ["pytest", "pytest-xdist", "setuptools"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ptyprocess"
|
name = "ptyprocess"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
description = "Run a subprocess in a pseudo terminal"
|
description = "Run a subprocess in a pseudo terminal"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
|
markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
|
||||||
files = [
|
files = [
|
||||||
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
|
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
|
||||||
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
|
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
|
||||||
@@ -913,6 +1002,7 @@ version = "0.2.3"
|
|||||||
description = "Safely evaluate AST nodes without side effects"
|
description = "Safely evaluate AST nodes without side effects"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
|
{file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
|
||||||
{file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
|
{file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
|
||||||
@@ -927,6 +1017,7 @@ version = "2.3.1"
|
|||||||
description = "A python package for Lib3MF tools"
|
description = "A python package for Lib3MF tools"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "py_lib3mf-2.3.1-py3-none-any.whl", hash = "sha256:86a870ef386debba9b74683d3a08125a34c153aaa65e967f61677cc5a0a65e24"},
|
{file = "py_lib3mf-2.3.1-py3-none-any.whl", hash = "sha256:86a870ef386debba9b74683d3a08125a34c153aaa65e967f61677cc5a0a65e24"},
|
||||||
]
|
]
|
||||||
@@ -937,6 +1028,7 @@ version = "1.16.4"
|
|||||||
description = "Python library for reading, writing and managing 3D objects in the Khronos Group gltf and gltf2 formats."
|
description = "Python library for reading, writing and managing 3D objects in the Khronos Group gltf and gltf2 formats."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pygltflib-1.16.4-py3-none-any.whl", hash = "sha256:1cb5886ca12ac512bbea3b5640f1e15575844418b1f1574de42ec19175e00f53"},
|
{file = "pygltflib-1.16.4-py3-none-any.whl", hash = "sha256:1cb5886ca12ac512bbea3b5640f1e15575844418b1f1574de42ec19175e00f53"},
|
||||||
{file = "pygltflib-1.16.4.tar.gz", hash = "sha256:7816253ef51b07a208864b94332bce8c509aa0f9b827d8771cf271293ecebb11"},
|
{file = "pygltflib-1.16.4.tar.gz", hash = "sha256:7816253ef51b07a208864b94332bce8c509aa0f9b827d8771cf271293ecebb11"},
|
||||||
@@ -952,6 +1044,7 @@ version = "2.19.2"
|
|||||||
description = "Pygments is a syntax highlighting package written in Python."
|
description = "Pygments is a syntax highlighting package written in Python."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
|
{file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
|
||||||
{file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
|
{file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
|
||||||
@@ -966,6 +1059,7 @@ version = "3.2.3"
|
|||||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"},
|
{file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"},
|
||||||
{file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"},
|
{file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"},
|
||||||
@@ -980,6 +1074,7 @@ version = "2.9.0.post0"
|
|||||||
description = "Extensions to the standard Python datetime module"
|
description = "Extensions to the standard Python datetime module"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
|
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
|
||||||
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
|
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
|
||||||
@@ -994,6 +1089,7 @@ version = "1.15.3"
|
|||||||
description = "Fundamental algorithms for scientific computing in Python"
|
description = "Fundamental algorithms for scientific computing in Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"},
|
{file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"},
|
||||||
{file = "scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253"},
|
{file = "scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253"},
|
||||||
@@ -1049,7 +1145,7 @@ numpy = ">=1.23.5,<2.5"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"]
|
dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"]
|
||||||
doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"]
|
doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"]
|
||||||
test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
|
test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
@@ -1057,6 +1153,7 @@ version = "1.17.0"
|
|||||||
description = "Python 2 and 3 compatibility utilities"
|
description = "Python 2 and 3 compatibility utilities"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
|
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
|
||||||
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
|
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
|
||||||
@@ -1068,6 +1165,7 @@ version = "0.6.3"
|
|||||||
description = "Extract data from python stack frames and tracebacks for informative displays"
|
description = "Extract data from python stack frames and tracebacks for informative displays"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
|
{file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
|
||||||
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
|
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
|
||||||
@@ -1087,6 +1185,7 @@ version = "1.9.6"
|
|||||||
description = "Svg Elements Parsing"
|
description = "Svg Elements Parsing"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "svgelements-1.9.6-py2.py3-none-any.whl", hash = "sha256:8a5cf2cc066d98e713d5b875b1d6e5eeb9b92e855e835ebd7caab2713ae1dcad"},
|
{file = "svgelements-1.9.6-py2.py3-none-any.whl", hash = "sha256:8a5cf2cc066d98e713d5b875b1d6e5eeb9b92e855e835ebd7caab2713ae1dcad"},
|
||||||
{file = "svgelements-1.9.6.tar.gz", hash = "sha256:7c02ad6404cd3d1771fd50e40fbfc0550b0893933466f86a6eb815f3ba3f37f7"},
|
{file = "svgelements-1.9.6.tar.gz", hash = "sha256:7c02ad6404cd3d1771fd50e40fbfc0550b0893933466f86a6eb815f3ba3f37f7"},
|
||||||
@@ -1098,6 +1197,7 @@ version = "1.7.1"
|
|||||||
description = "A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves."
|
description = "A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "svgpathtools-1.7.1-py2.py3-none-any.whl", hash = "sha256:3cbb8ba0e8d200f9639034608d9c55b68efbc1bef99ea99559a3e7cb024fb738"},
|
{file = "svgpathtools-1.7.1-py2.py3-none-any.whl", hash = "sha256:3cbb8ba0e8d200f9639034608d9c55b68efbc1bef99ea99559a3e7cb024fb738"},
|
||||||
{file = "svgpathtools-1.7.1.tar.gz", hash = "sha256:beaef20fd78164aa5f0a7d4fd164ef20cb0d3d015cdec50c8c168e9d6547f041"},
|
{file = "svgpathtools-1.7.1.tar.gz", hash = "sha256:beaef20fd78164aa5f0a7d4fd164ef20cb0d3d015cdec50c8c168e9d6547f041"},
|
||||||
@@ -1114,17 +1214,79 @@ version = "1.4.3"
|
|||||||
description = "A Python library to create SVG drawings."
|
description = "A Python library to create SVG drawings."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "svgwrite-1.4.3-py3-none-any.whl", hash = "sha256:bb6b2b5450f1edbfa597d924f9ac2dd099e625562e492021d7dd614f65f8a22d"},
|
{file = "svgwrite-1.4.3-py3-none-any.whl", hash = "sha256:bb6b2b5450f1edbfa597d924f9ac2dd099e625562e492021d7dd614f65f8a22d"},
|
||||||
{file = "svgwrite-1.4.3.zip", hash = "sha256:a8fbdfd4443302a6619a7f76bc937fc683daf2628d9b737c891ec08b8ce524c3"},
|
{file = "svgwrite-1.4.3.zip", hash = "sha256:a8fbdfd4443302a6619a7f76bc937fc683daf2628d9b737c891ec08b8ce524c3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "taskipy"
|
||||||
|
version = "1.14.1"
|
||||||
|
description = "tasks runner for python projects"
|
||||||
|
optional = false
|
||||||
|
python-versions = "<4.0,>=3.6"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "taskipy-1.14.1-py3-none-any.whl", hash = "sha256:6e361520f29a0fd2159848e953599f9c75b1d0b047461e4965069caeb94908f1"},
|
||||||
|
{file = "taskipy-1.14.1.tar.gz", hash = "sha256:410fbcf89692dfd4b9f39c2b49e1750b0a7b81affd0e2d7ea8c35f9d6a4774ed"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = ">=0.4.4,<0.5.0"
|
||||||
|
mslex = {version = ">=1.1.0,<2.0.0", markers = "sys_platform == \"win32\""}
|
||||||
|
psutil = ">=5.7.2,<7"
|
||||||
|
tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tomli"
|
||||||
|
version = "2.2.1"
|
||||||
|
description = "A lil' TOML parser"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
groups = ["dev"]
|
||||||
|
files = [
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
|
||||||
|
{file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
|
||||||
|
{file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
|
||||||
|
{file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
|
||||||
|
{file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
|
||||||
|
{file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "traitlets"
|
name = "traitlets"
|
||||||
version = "5.14.3"
|
version = "5.14.3"
|
||||||
description = "Traitlets Python configuration system"
|
description = "Traitlets Python configuration system"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
|
{file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
|
||||||
{file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
|
{file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
|
||||||
@@ -1140,6 +1302,7 @@ version = "1.2"
|
|||||||
description = "Find all the sides and angles of a triangle, if you know some of the sides and/or angles. (Uses the Law of Sines and Law of Cosines.)"
|
description = "Find all the sides and angles of a triangle, if you know some of the sides and/or angles. (Uses the Law of Sines and Law of Cosines.)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "trianglesolver-1.2-py3-none-any.whl", hash = "sha256:aa0903c3708b4e2b496f06d490cae72c6ff6274b00d1edce420fcfa3b2b76682"},
|
{file = "trianglesolver-1.2-py3-none-any.whl", hash = "sha256:aa0903c3708b4e2b496f06d490cae72c6ff6274b00d1edce420fcfa3b2b76682"},
|
||||||
{file = "trianglesolver-1.2.tar.gz", hash = "sha256:4af18aade579d5c0d64389b3e65aeaf06cff26319762ccd859e3268559a76aea"},
|
{file = "trianglesolver-1.2.tar.gz", hash = "sha256:4af18aade579d5c0d64389b3e65aeaf06cff26319762ccd859e3268559a76aea"},
|
||||||
@@ -1151,6 +1314,7 @@ version = "4.14.1"
|
|||||||
description = "Backported and Experimental Type Hints for Python 3.9+"
|
description = "Backported and Experimental Type Hints for Python 3.9+"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"},
|
{file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"},
|
||||||
{file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"},
|
{file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"},
|
||||||
@@ -1162,6 +1326,7 @@ version = "0.9.0"
|
|||||||
description = "Runtime inspection utilities for typing module."
|
description = "Runtime inspection utilities for typing module."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"},
|
{file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"},
|
||||||
{file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"},
|
{file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"},
|
||||||
@@ -1177,6 +1342,7 @@ version = "9.3.1"
|
|||||||
description = "VTK is an open-source toolkit for 3D computer graphics, image processing, and visualization"
|
description = "VTK is an open-source toolkit for 3D computer graphics, image processing, and visualization"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "vtk-9.3.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:c41ed344b9cc90ee9dcfc5967815de272985647d0c8e0a57f0e8b4229bc1b0b9"},
|
{file = "vtk-9.3.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:c41ed344b9cc90ee9dcfc5967815de272985647d0c8e0a57f0e8b4229bc1b0b9"},
|
||||||
{file = "vtk-9.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84c04327becc4c4dfe1fb04248baa4b5c480f188a9d52f4b912b163d33622442"},
|
{file = "vtk-9.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84c04327becc4c4dfe1fb04248baa4b5c480f188a9d52f4b912b163d33622442"},
|
||||||
@@ -1218,6 +1384,7 @@ version = "0.2.13"
|
|||||||
description = "Measures the displayed width of unicode strings in a terminal"
|
description = "Measures the displayed width of unicode strings in a terminal"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
|
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
|
||||||
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
|
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
|
||||||
@@ -1229,6 +1396,7 @@ version = "1.17.2"
|
|||||||
description = "Module for decorators, wrappers and monkey patching."
|
description = "Module for decorators, wrappers and monkey patching."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"},
|
{file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"},
|
||||||
{file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"},
|
{file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"},
|
||||||
@@ -1312,6 +1480,6 @@ files = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.1"
|
||||||
python-versions = ">=3.10,<3.13"
|
python-versions = ">=3.10,<3.13"
|
||||||
content-hash = "93d841ad150ea31f388ae73a764e7a0491e65ace01fa1bad7b66dbf6cba4b459"
|
content-hash = "17564ce50af9a75e84337c83f77d86c9d2ff73a366de70ba27453e529bb468e9"
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core==2.1.3", "taskipy==1.14.1"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "yacv-server"
|
name = "yacv-server"
|
||||||
version = "0.9.7"
|
version = "0.10.0-alpha.4"
|
||||||
description = "Yet Another CAD Viewer (server)"
|
description = "Yet Another CAD Viewer (server)"
|
||||||
authors = ["Yeicor <4929005+Yeicor@users.noreply.github.com>"]
|
authors = ["Yeicor <4929005+Yeicor@users.noreply.github.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
include = [
|
include = [
|
||||||
{ path = 'yacv_server/frontend/*', format = 'wheel' },
|
{ path = 'yacv_server/frontend/**/*', format = 'wheel' },
|
||||||
{ path = 'yacv_server/frontend/*', format = 'sdist' },
|
{ path = 'yacv_server/frontend/**/*', format = 'sdist' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.taskipy.tasks]
|
||||||
|
build = "task build_frontend && task build_backend"
|
||||||
|
build_frontend = "rm -rf yacv_server/frontend || true && yarn install && YACV_SMALL_BUILD=true yarn build --outDir yacv_server/frontend"
|
||||||
|
build_backend = "poetry build --format wheel"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = ">=3.10,<3.13" # Due to vtk transitive dependency of build123d -> cadquery-ocp -> vtk
|
python = ">=3.10,<3.13" # Due to vtk transitive dependency of build123d -> cadquery-ocp -> vtk
|
||||||
|
|
||||||
@@ -19,11 +28,7 @@ build123d = ">=0.9,<0.10"
|
|||||||
# Misc
|
# Misc
|
||||||
pygltflib = "^1.16.2"
|
pygltflib = "^1.16.2"
|
||||||
pillow = ">=10.2,<12.0"
|
pillow = ">=10.2,<12.0"
|
||||||
|
poetry-core = "==2.1.3"
|
||||||
|
|
||||||
[tool.poetry.build]
|
[tool.poetry.group.dev.dependencies]
|
||||||
generate-setup-file = false
|
taskipy = "^1.14.1"
|
||||||
script = "build.py"
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core"]
|
|
||||||
build-backend = "poetry.core.masonry.api"
|
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import {fileURLToPath, URL} from 'node:url'
|
import {fileURLToPath, URL} from 'node:url'
|
||||||
|
|
||||||
import {defineConfig} from 'vite'
|
import {defineConfig} from 'vite'
|
||||||
|
// @ts-ignore
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
// @ts-ignore
|
||||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
import {name, version} from './package.json'
|
import {name, version} from './package.json'
|
||||||
import {execSync} from 'child_process'
|
import {execSync} from 'child_process'
|
||||||
import {viteStaticCopy} from "vite-plugin-static-copy";
|
import {viteStaticCopy} from "vite-plugin-static-copy";
|
||||||
import {dirname, join} from "path";
|
import {dirname, join} from "path";
|
||||||
|
import {version as pyodideVersion} from "pyodide";
|
||||||
|
|
||||||
|
let wantsSmallBuild = process.env.YACV_SMALL_BUILD == "true";
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -15,7 +20,7 @@ export default defineConfig({
|
|||||||
vue({
|
vue({
|
||||||
template: {
|
template: {
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
isCustomElement: tag => tag == 'model-viewer'
|
isCustomElement: (tag: string) => tag == 'model-viewer'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -37,8 +42,17 @@ export default defineConfig({
|
|||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
experimentalMinChunkSize: 512000, // 512KB (avoid too many small chunks)
|
experimentalMinChunkSize: 512000, // 512KB (avoid too many small chunks)
|
||||||
}
|
},
|
||||||
}
|
external: wantsSmallBuild ? [
|
||||||
|
// Exclude some large optional dependencies if small build is requested (for embedding in python package)
|
||||||
|
"pyodide",
|
||||||
|
/.*\/pyodide-worker.*/,
|
||||||
|
"monaco-editor",
|
||||||
|
/monaco-editor\/.*/,
|
||||||
|
"@guolao/vue-monaco-editor",
|
||||||
|
/three\/examples\/jsm\/libs\/draco\/draco_(en|de)coder\.js/,
|
||||||
|
] : [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
worker: {
|
worker: {
|
||||||
format: 'es', // Use ES modules for workers (IIFE is not supported with code-splitting)
|
format: 'es', // Use ES modules for workers (IIFE is not supported with code-splitting)
|
||||||
@@ -48,6 +62,7 @@ export default defineConfig({
|
|||||||
__APP_VERSION__: JSON.stringify(version),
|
__APP_VERSION__: JSON.stringify(version),
|
||||||
__APP_GIT_SHA__: JSON.stringify(execSync('git rev-parse HEAD').toString().trim()),
|
__APP_GIT_SHA__: JSON.stringify(execSync('git rev-parse HEAD').toString().trim()),
|
||||||
__APP_GIT_DIRTY__: JSON.stringify(execSync('git diff --quiet || echo dirty').toString().trim()),
|
__APP_GIT_DIRTY__: JSON.stringify(execSync('git diff --quiet || echo dirty').toString().trim()),
|
||||||
|
__YACV_SMALL_BUILD__: JSON.stringify(wantsSmallBuild),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -59,12 +74,13 @@ function viteStaticCopyPyodide() {
|
|||||||
"!**/*.whl",
|
"!**/*.whl",
|
||||||
"!**/node_modules",
|
"!**/node_modules",
|
||||||
];
|
];
|
||||||
|
// @ts-ignore
|
||||||
const pyodideDir = dirname(fileURLToPath(import.meta.resolve("pyodide")));
|
const pyodideDir = dirname(fileURLToPath(import.meta.resolve("pyodide")));
|
||||||
return viteStaticCopy({
|
return viteStaticCopy({
|
||||||
targets: [
|
targets: wantsSmallBuild ? [] : [
|
||||||
{
|
{
|
||||||
src: [join(pyodideDir, "*")].concat(PYODIDE_EXCLUDE),
|
src: [join(pyodideDir, "*")].concat(PYODIDE_EXCLUDE),
|
||||||
dest: "assets",
|
dest: "pyodide-v" + pyodideVersion, // It would be better to use hashed names instead of folder...
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ class BufferedPubSub(Generic[T]):
|
|||||||
self._subscribers.remove(q)
|
self._subscribers.remove(q)
|
||||||
logger.debug(f"Unsubscribed from %s (%d subscribers)", self, len(self._subscribers))
|
logger.debug(f"Unsubscribed from %s (%d subscribers)", self, len(self._subscribers))
|
||||||
|
|
||||||
def subscribe(self, include_buffered: bool = True, include_future: bool = True, yield_timeout: float = 0.0) -> \
|
def subscribe(self, include_buffered: bool = True, include_future: bool = True,
|
||||||
Generator[T, None, None]:
|
yield_timeout: float | None = 0.0) -> Generator[T, None, None]:
|
||||||
"""Subscribes to events as a generator that yields events and automatically unsubscribes"""
|
"""Subscribes to events as a generator that yields events and automatically unsubscribes"""
|
||||||
q = self._subscribe(include_buffered, include_future)
|
q = self._subscribe(include_buffered, include_future)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ from typing import List, Dict, Tuple, Optional
|
|||||||
from OCP.BRep import BRep_Tool
|
from OCP.BRep import BRep_Tool
|
||||||
from OCP.BRepAdaptor import BRepAdaptor_Curve
|
from OCP.BRepAdaptor import BRepAdaptor_Curve
|
||||||
from OCP.GCPnts import GCPnts_TangentialDeflection
|
from OCP.GCPnts import GCPnts_TangentialDeflection
|
||||||
from OCP.OCP.BRepLib import BRepLib_ToolTriangulatedShape
|
from OCP.BRepLib import BRepLib_ToolTriangulatedShape
|
||||||
from OCP.OCP.TopAbs import TopAbs_Orientation
|
from OCP.TopAbs import TopAbs_Orientation
|
||||||
from OCP.TopLoc import TopLoc_Location
|
from OCP.TopLoc import TopLoc_Location
|
||||||
from OCP.TopoDS import TopoDS_Face, TopoDS_Edge, TopoDS_Shape, TopoDS_Vertex
|
from OCP.TopoDS import TopoDS_Face, TopoDS_Edge, TopoDS_Shape, TopoDS_Vertex
|
||||||
from build123d import Vertex, Face, Location, Compound, Vector
|
from build123d import Vertex, Face, Location, Compound, Vector
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum, auto
|
||||||
from http.server import ThreadingHTTPServer
|
from http.server import ThreadingHTTPServer
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@@ -64,10 +65,20 @@ class UpdatesApiFullData(UpdatesApiData):
|
|||||||
return super().to_json()
|
return super().to_json()
|
||||||
|
|
||||||
|
|
||||||
|
class YACVProtocol(Enum):
|
||||||
|
"""Enum of communication protocols supported by the server"""
|
||||||
|
HTTP = auto()
|
||||||
|
"""The recommended protocol for any platform that can run a web server."""
|
||||||
|
STDERR = auto()
|
||||||
|
"""Prints the updates one by one to stderr (first metadata, then base64 of glb file) using a special prefix. Required for Pyodide support."""
|
||||||
|
|
||||||
|
|
||||||
class YACV:
|
class YACV:
|
||||||
"""The main yacv_server class, which manages the web server and the CAD objects."""
|
"""The main yacv_server class, which manages the web server and the CAD objects."""
|
||||||
|
|
||||||
# Startup
|
# Startup
|
||||||
|
protocol: YACVProtocol
|
||||||
|
"""The protocol used by the server. Defaults to HTTP, but can be set to STDERR for Pyodide support."""
|
||||||
server_thread: Optional[Thread]
|
server_thread: Optional[Thread]
|
||||||
"""The main thread running the server (will spawn other threads for each request)"""
|
"""The main thread running the server (will spawn other threads for each request)"""
|
||||||
server: Optional[ThreadingHTTPServer]
|
server: Optional[ThreadingHTTPServer]
|
||||||
@@ -127,6 +138,10 @@ class YACV:
|
|||||||
in the hexadecimal format #RRGGBB or #RRGGBBAA."""
|
in the hexadecimal format #RRGGBB or #RRGGBBAA."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initializes the YACV server"""
|
||||||
|
raw_protocol = os.getenv('YACV_PROTOCOL', 'http' if sys.platform != 'emscripten' else 'stderr').upper()
|
||||||
|
self.protocol = YACVProtocol[raw_protocol] if raw_protocol in YACVProtocol.__members__ else YACVProtocol.HTTP
|
||||||
|
self.protocol = YACVProtocol.STDERR
|
||||||
self.server_thread = None
|
self.server_thread = None
|
||||||
self.server = None
|
self.server = None
|
||||||
self.startup_complete = threading.Event()
|
self.startup_complete = threading.Event()
|
||||||
@@ -144,6 +159,7 @@ class YACV:
|
|||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Starts the web server in the background"""
|
"""Starts the web server in the background"""
|
||||||
|
if self.protocol == YACVProtocol.STDERR: return # No server to start, just print to stderr
|
||||||
assert self.server_thread is None, "Server currently running, cannot start another one"
|
assert self.server_thread is None, "Server currently running, cannot start another one"
|
||||||
assert self.startup_complete.is_set() is False, "Server already started"
|
assert self.startup_complete.is_set() is False, "Server already started"
|
||||||
# Start the server in a separate daemon thread
|
# Start the server in a separate daemon thread
|
||||||
@@ -160,6 +176,8 @@ class YACV:
|
|||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
def stop(self, *args):
|
def stop(self, *args):
|
||||||
"""Stops the web server"""
|
"""Stops the web server"""
|
||||||
|
if self.protocol == YACVProtocol.STDERR: return # No server to stop, just print to stderr
|
||||||
|
# The remainder is for the HTTP protocol only
|
||||||
if self.server_thread is None:
|
if self.server_thread is None:
|
||||||
logger.error('Cannot stop server because it is not running')
|
logger.error('Cannot stop server because it is not running')
|
||||||
return
|
return
|
||||||
@@ -167,7 +185,7 @@ class YACV:
|
|||||||
# Inform the server that we are shutting down
|
# Inform the server that we are shutting down
|
||||||
self.shutting_down.set()
|
self.shutting_down.set()
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
self.show_events.publish(UpdatesApiFullData(name='__shutdown', _hash='', is_remove=None, obj=None))
|
self._show_event(UpdatesApiFullData(name='__shutdown', _hash='', is_remove=None, obj=None))
|
||||||
|
|
||||||
# If we were too fast, ensure that at least one client has connected
|
# If we were too fast, ensure that at least one client has connected
|
||||||
graceful_secs_connect = float(os.getenv('YACV_GRACEFUL_SECS_CONNECT', 12.0))
|
graceful_secs_connect = float(os.getenv('YACV_GRACEFUL_SECS_CONNECT', 12.0))
|
||||||
@@ -195,9 +213,11 @@ class YACV:
|
|||||||
if len(args) >= 1 and args[0] in (signal.SIGINT, signal.SIGTERM):
|
if len(args) >= 1 and args[0] in (signal.SIGINT, signal.SIGTERM):
|
||||||
sys.exit(0) # Exit with success
|
sys.exit(0) # Exit with success
|
||||||
|
|
||||||
|
_yacvServerModelPrefix = "yacv_server://model/"
|
||||||
|
|
||||||
def _run_server(self):
|
def _run_server(self):
|
||||||
"""Runs the web server"""
|
"""Runs the web server"""
|
||||||
logger.info('Starting server...')
|
logger.info('Starting server in %s mode...', self.protocol.name)
|
||||||
self.server = ThreadingHTTPServer(
|
self.server = ThreadingHTTPServer(
|
||||||
(os.getenv('YACV_HOST', 'localhost'), int(os.getenv('YACV_PORT', 32323))),
|
(os.getenv('YACV_HOST', 'localhost'), int(os.getenv('YACV_PORT', 32323))),
|
||||||
lambda a, b, c: HTTPHandler(a, b, c, yacv=self))
|
lambda a, b, c: HTTPHandler(a, b, c, yacv=self))
|
||||||
@@ -206,6 +226,22 @@ class YACV:
|
|||||||
self.startup_complete.set()
|
self.startup_complete.set()
|
||||||
self.server.serve_forever()
|
self.server.serve_forever()
|
||||||
|
|
||||||
|
def _show_event(self, event: UpdatesApiFullData):
|
||||||
|
"""Handles a show event by publishing it to the show events buffer (and special handling for stderr protocol)."""
|
||||||
|
self.show_events.publish(event)
|
||||||
|
# If the protocol is STDERR, we need to print the event to stderr
|
||||||
|
if self.protocol == YACVProtocol.STDERR:
|
||||||
|
msg = f'{self._yacvServerModelPrefix}{event.to_json()}'
|
||||||
|
if not event.is_remove:
|
||||||
|
# Always build the object even if the interface already has it (optimization disabled for Pyodide)
|
||||||
|
glb_and_hash = self.export(event.name)
|
||||||
|
if glb_and_hash is None:
|
||||||
|
logger.warning('Object %s not found, ignoring it...', event.name)
|
||||||
|
return
|
||||||
|
glb = glb_and_hash[0]
|
||||||
|
msg += f'{base64.b64encode(glb).decode("utf-8")}'
|
||||||
|
print(msg, file=sys.stderr, flush=True)
|
||||||
|
|
||||||
def show(self, *objs: List[YACVSupported], names: Optional[Union[str, List[str]]] = None, **kwargs):
|
def show(self, *objs: List[YACVSupported], names: Optional[Union[str, List[str]]] = None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Shows the given CAD objects in the frontend. The objects will be tessellated and converted to GLTF. Optionally,
|
Shows the given CAD objects in the frontend. The objects will be tessellated and converted to GLTF. Optionally,
|
||||||
@@ -252,13 +288,13 @@ class YACV:
|
|||||||
# Some properties may be lost in preprocessing, so save them in kwargs
|
# Some properties may be lost in preprocessing, so save them in kwargs
|
||||||
_kwargs = kwargs.copy()
|
_kwargs = kwargs.copy()
|
||||||
if obj_color is not None:
|
if obj_color is not None:
|
||||||
_kwargs['color_obj'] = obj_color # Only applies to highest-dimensional objects
|
_kwargs['color_obj'] = obj_color # Only applies to highest-dimensional objects
|
||||||
_kwargs['texture'] = _read_texture_uri(getattr(obj, 'yacv_texture', None) or kwargs.get('texture', None))
|
_kwargs['texture'] = _read_texture_uri(getattr(obj, 'yacv_texture', None) or kwargs.get('texture', None))
|
||||||
if not isinstance(obj, bytes):
|
if not isinstance(obj, bytes):
|
||||||
obj = _preprocess_cad(obj, **_kwargs)
|
obj = _preprocess_cad(obj, **_kwargs)
|
||||||
_hash = _hashcode(obj, **_kwargs)
|
_hash = _hashcode(obj, **_kwargs)
|
||||||
event = UpdatesApiFullData(name=name, _hash=_hash, obj=obj, kwargs=_kwargs or {})
|
event = UpdatesApiFullData(name=name, _hash=_hash, obj=obj, kwargs=_kwargs or {})
|
||||||
self.show_events.publish(event)
|
self._show_event(event)
|
||||||
|
|
||||||
logger.info('show %s took %.3f seconds', names, time.time() - start)
|
logger.info('show %s took %.3f seconds', names, time.time() - start)
|
||||||
|
|
||||||
@@ -283,7 +319,7 @@ class YACV:
|
|||||||
# Publish the remove event
|
# Publish the remove event
|
||||||
show_event = copy.copy(show_events[-1])
|
show_event = copy.copy(show_events[-1])
|
||||||
show_event.is_remove = True
|
show_event.is_remove = True
|
||||||
self.show_events.publish(show_event)
|
self._show_event(show_event)
|
||||||
|
|
||||||
def clear(self, except_names: List[str] = None):
|
def clear(self, except_names: List[str] = None):
|
||||||
"""Clears all previously-shown objects from the scene"""
|
"""Clears all previously-shown objects from the scene"""
|
||||||
|
|||||||
15
yarn.lock
15
yarn.lock
@@ -1001,6 +1001,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.21.0"
|
undici-types "~6.21.0"
|
||||||
|
|
||||||
|
"@types/pako@^2.0.3":
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pako/-/pako-2.0.3.tgz#b6993334f3af27c158f3fe0dfeeba987c578afb1"
|
||||||
|
integrity sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==
|
||||||
|
|
||||||
"@types/stats.js@*":
|
"@types/stats.js@*":
|
||||||
version "0.17.4"
|
version "0.17.4"
|
||||||
resolved "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz"
|
resolved "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz"
|
||||||
@@ -1944,6 +1949,11 @@ jackspeak@^4.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@isaacs/cliui" "^8.0.2"
|
"@isaacs/cliui" "^8.0.2"
|
||||||
|
|
||||||
|
js-base64@^3.7.7:
|
||||||
|
version "3.7.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79"
|
||||||
|
integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==
|
||||||
|
|
||||||
js-tokens@^4.0.0:
|
js-tokens@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
|
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
|
||||||
@@ -2431,6 +2441,11 @@ pacote@^21.0.0:
|
|||||||
ssri "^12.0.0"
|
ssri "^12.0.0"
|
||||||
tar "^6.1.11"
|
tar "^6.1.11"
|
||||||
|
|
||||||
|
pako@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
|
||||||
|
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
|
||||||
|
|
||||||
parent-module@^1.0.0:
|
parent-module@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user