mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b6d3f6266 | ||
|
|
255ae72ed2 | ||
|
|
77dd9fb43e | ||
|
|
5dc2ae2f8d | ||
|
|
58440723bd | ||
|
|
bfdd656316 | ||
|
|
7408823c02 | ||
|
|
856ffbc4c5 |
@@ -93,7 +93,7 @@ export class NetworkManager extends EventTarget {
|
||||
|
||||
private foundModel(name: string, hash: string | null, url: string, isRemove: boolean | null, disconnect: () => void = () => {
|
||||
}) {
|
||||
console.debug("Found model", name, "with hash", hash, "at", url, "isRemove", isRemove);
|
||||
// console.debug("Found model", name, "with hash", hash, "at", url, "isRemove", isRemove);
|
||||
|
||||
// We only care about the latest update per model name
|
||||
this.bufferedUpdates = this.bufferedUpdates.filter(m => m.name !== name);
|
||||
|
||||
@@ -13,23 +13,25 @@ export class SceneMgr {
|
||||
let loadStart = performance.now();
|
||||
let loadNetworkEnd: number;
|
||||
|
||||
// Start merging into the current document, replacing or adding as needed
|
||||
document = await mergePartial(url, name, document, () => loadNetworkEnd = performance.now());
|
||||
try {
|
||||
// Start merging into the current document, replacing or adding as needed
|
||||
document = await mergePartial(url, name, document, () => loadNetworkEnd = performance.now());
|
||||
|
||||
console.log("Model", name, "loaded in", performance.now() - loadNetworkEnd!, "ms after",
|
||||
loadNetworkEnd! - loadStart, "ms of transferring data (maybe building the object on the server)");
|
||||
console.log("Model", name, "loaded in", performance.now() - loadNetworkEnd!, "ms after",
|
||||
loadNetworkEnd! - loadStart, "ms of transferring data (maybe building the object on the server)");
|
||||
} finally {
|
||||
if (updateHelpers) {
|
||||
// Reload the helpers to fit the new model
|
||||
await this.reloadHelpers(sceneUrl, document, reloadScene);
|
||||
reloadScene = false;
|
||||
}
|
||||
|
||||
if (updateHelpers) {
|
||||
// Reload the helpers to fit the new model
|
||||
await this.reloadHelpers(sceneUrl, document, reloadScene);
|
||||
reloadScene = false;
|
||||
}
|
||||
|
||||
if (reloadScene) {
|
||||
// Display the final fully loaded model
|
||||
let displayStart = performance.now();
|
||||
document = await this.showCurrentDoc(sceneUrl, document);
|
||||
console.log("Scene displayed in", performance.now() - displayStart, "ms");
|
||||
if (reloadScene) {
|
||||
// Display the final fully loaded model
|
||||
let displayStart = performance.now();
|
||||
document = await this.showCurrentDoc(sceneUrl, document);
|
||||
console.log("Scene displayed in", performance.now() - displayStart, "ms");
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
@@ -97,7 +99,7 @@ export class SceneMgr {
|
||||
// Serialize the document into a GLB and update the viewerSrc
|
||||
let buffer = await toBuffer(document);
|
||||
let blob = new Blob([buffer], {type: 'model/gltf-binary'});
|
||||
console.debug("Showing current doc", document, "as", Array.from(buffer));
|
||||
console.debug("Showing current doc", document, "with", buffer.length, "total bytes");
|
||||
sceneUrl.value = URL.createObjectURL(blob);
|
||||
|
||||
return document;
|
||||
|
||||
@@ -43,10 +43,11 @@ const emit = defineEmits<{ remove: [] }>()
|
||||
|
||||
let modelName = props.meshes[0].getExtras()[extrasNameKey] // + " blah blah blah blah blag blah blah blah"
|
||||
|
||||
// Reactive properties
|
||||
const enabledFeatures = defineModel<Array<number>>("enabledFeatures", {default: [0, 1, 2]});
|
||||
const opacity = defineModel<number>("opacity", {default: 1});
|
||||
const wireframe = ref(false);
|
||||
// Count the number of faces, edges and vertices
|
||||
let faceCount = ref(-1);
|
||||
let edgeCount = ref(-1);
|
||||
let vertexCount = ref(-1);
|
||||
|
||||
// Clipping planes are handled in y-up space (swapped on interface, Z inverted later)
|
||||
const clipPlaneX = ref(1);
|
||||
const clipPlaneSwappedX = ref(false);
|
||||
@@ -56,24 +57,10 @@ const clipPlaneZ = ref(1);
|
||||
const clipPlaneSwappedZ = ref(false);
|
||||
const edgeWidth = ref(settings.edgeWidth);
|
||||
|
||||
// Count the number of faces, edges and vertices
|
||||
let faceCount = props.meshes
|
||||
.flatMap((m) => m.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.TRIANGLES))
|
||||
.map(p => (p.getExtras()?.face_triangles_end as any)?.length ?? 1)
|
||||
.reduce((a, b) => a + b, 0)
|
||||
let edgeCount = props.meshes
|
||||
.flatMap((m) => m.listPrimitives().filter(p => p.getMode() in [WebGL2RenderingContext.LINE_STRIP, WebGL2RenderingContext.LINES]))
|
||||
.map(p => (p.getExtras()?.edge_points_end as any)?.length ?? 0)
|
||||
.reduce((a, b) => a + b, 0)
|
||||
let vertexCount = props.meshes
|
||||
.flatMap((m) => m.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.POINTS))
|
||||
.map(p => (p.getAttribute("POSITION")?.getCount() ?? 0))
|
||||
.reduce((a, b) => a + b, 0)
|
||||
|
||||
// Set initial defaults for the enabled features
|
||||
if (faceCount === 0) enabledFeatures.value = enabledFeatures.value.filter((f) => f !== 0)
|
||||
if (edgeCount === 0) enabledFeatures.value = enabledFeatures.value.filter((f) => f !== 1)
|
||||
if (vertexCount === 0) enabledFeatures.value = enabledFeatures.value.filter((f) => f !== 2)
|
||||
// Misc properties
|
||||
const enabledFeatures = defineModel<Array<number>>("enabledFeatures", {default: [0, 1, 2]});
|
||||
const opacity = defineModel<number>("opacity", {default: 1});
|
||||
const wireframe = ref(false);
|
||||
|
||||
// Listeners for changes in the properties (or viewer reloads)
|
||||
function onEnabledFeaturesChange(newEnabledFeatures: Array<number>) {
|
||||
@@ -81,9 +68,6 @@ function onEnabledFeaturesChange(newEnabledFeatures: Array<number>) {
|
||||
let scene = props.viewer?.scene;
|
||||
let sceneModel = (scene as any)?._model;
|
||||
if (!scene || !sceneModel) return;
|
||||
// Iterate all primitives of the mesh and set their visibility based on the enabled features
|
||||
// Use the scene graph instead of the document to avoid reloading the same model, at the cost
|
||||
// of not actually removing the primitives from the scene graph
|
||||
sceneModel.traverse((child: MObject3D) => {
|
||||
if (child.userData[extrasNameKey] === modelName) {
|
||||
let childIsFace = child.type == 'Mesh' || child.type == 'SkinnedMesh'
|
||||
@@ -107,10 +91,6 @@ function onOpacityChange(newOpacity: number) {
|
||||
let scene = props.viewer?.scene;
|
||||
let sceneModel = (scene as any)?._model;
|
||||
if (!scene || !sceneModel) return;
|
||||
// Iterate all primitives of the mesh and set their opacity based on the enabled features
|
||||
// Use the scene graph instead of the document to avoid reloading the same model, at the cost
|
||||
// of not actually removing the primitives from the scene graph
|
||||
// console.log('Opacity may have changed', newOpacity)
|
||||
sceneModel.traverse((child: MObject3D) => {
|
||||
if (child.userData[extrasNameKey] === modelName) {
|
||||
if (child.material && child.material.opacity !== newOpacity) {
|
||||
@@ -129,10 +109,6 @@ function onWireframeChange(newWireframe: boolean) {
|
||||
let scene = props.viewer?.scene;
|
||||
let sceneModel = (scene as any)?._model;
|
||||
if (!scene || !sceneModel) return;
|
||||
// Iterate all primitives of the mesh and set their wireframe based on the enabled features
|
||||
// Use the scene graph instead of the document to avoid reloading the same model, at the cost
|
||||
// of not actually removing the primitives from the scene graph
|
||||
// console.log('Wireframe may have changed', newWireframe)
|
||||
sceneModel.traverse((child: MObject3D) => {
|
||||
if (child.userData[extrasNameKey] === modelName) {
|
||||
if (child.material && child.material.wireframe !== newWireframe) {
|
||||
@@ -268,9 +244,8 @@ function onModelLoad() {
|
||||
let scene = props.viewer?.scene;
|
||||
let sceneModel = (scene as any)?._model;
|
||||
if (!scene || !sceneModel) return;
|
||||
// Iterate all primitives of the mesh and set their visibility based on the enabled features
|
||||
// Use the scene graph instead of the document to avoid reloading the same model, at the cost
|
||||
// of not actually removing the primitives from the scene graph
|
||||
|
||||
// Add darkened back faces for all face objects to improve cutting planes
|
||||
let childrenToAdd: Array<MObject3D> = [];
|
||||
sceneModel.traverse((child: MObject3D) => {
|
||||
if (child.userData[extrasNameKey] === modelName) {
|
||||
@@ -303,6 +278,28 @@ function onModelLoad() {
|
||||
});
|
||||
childrenToAdd.forEach((child: MObject3D) => sceneModel.add(child));
|
||||
|
||||
// Count the number of faces, edges and vertices
|
||||
faceCount.value = props.meshes
|
||||
.flatMap((m) => m.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.TRIANGLES))
|
||||
.map(p => (p.getExtras()?.face_triangles_end as any)?.length ?? 1)
|
||||
.reduce((a, b) => a + b, 0)
|
||||
edgeCount.value = props.meshes
|
||||
.flatMap((m) => m.listPrimitives().filter(p => p.getMode() in [WebGL2RenderingContext.LINE_STRIP, WebGL2RenderingContext.LINES]))
|
||||
.map(p => (p.getExtras()?.edge_points_end as any)?.length ?? 0)
|
||||
.reduce((a, b) => a + b, 0)
|
||||
vertexCount.value = props.meshes
|
||||
.flatMap((m) => m.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.POINTS))
|
||||
.map(p => (p.getAttribute("POSITION")?.getCount() ?? 0))
|
||||
.reduce((a, b) => a + b, 0)
|
||||
|
||||
// Set the enabled features to all provided features
|
||||
if (faceCount.value === 0) enabledFeatures.value = enabledFeatures.value.filter((f) => f !== 0)
|
||||
else if (!enabledFeatures.value.includes(0)) enabledFeatures.value.push(0)
|
||||
if (edgeCount.value === 0) enabledFeatures.value = enabledFeatures.value.filter((f) => f !== 1)
|
||||
else if (!enabledFeatures.value.includes(1)) enabledFeatures.value.push(1)
|
||||
if (vertexCount.value === 0) enabledFeatures.value = enabledFeatures.value.filter((f) => f !== 2)
|
||||
else if (!enabledFeatures.value.includes(2)) enabledFeatures.value.push(2)
|
||||
|
||||
// Furthermore...
|
||||
// Enabled features may have been reset after a reload
|
||||
onEnabledFeaturesChange(enabledFeatures.value)
|
||||
|
||||
@@ -36,9 +36,7 @@ function onRemove(mesh: Mesh) {
|
||||
}
|
||||
|
||||
function findModel(name: string) {
|
||||
console.log('Find model', name);
|
||||
if (!expandedNames.value.includes(name)) expandedNames.value.push(name);
|
||||
console.log('Expanded', expandedNames.value);
|
||||
}
|
||||
|
||||
defineExpose({findModel})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yet-another-cad-viewer",
|
||||
"version": "0.8.3",
|
||||
"version": "0.8.5",
|
||||
"description": "",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "yacv-server"
|
||||
version = "0.8.3"
|
||||
version = "0.8.5"
|
||||
description = "Yet Another CAD Viewer (server)"
|
||||
authors = ["Yeicor <4929005+Yeicor@users.noreply.github.com>"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
Utilities to work with CAD objects
|
||||
"""
|
||||
import hashlib
|
||||
from typing import Optional, Union, List, Tuple
|
||||
from typing import Optional, Union, Tuple
|
||||
|
||||
from OCP.TopLoc import TopLoc_Location
|
||||
from OCP.TopoDS import TopoDS_Shape
|
||||
from build123d import Compound, Shape
|
||||
|
||||
from yacv_server.gltf import GLTFMgr
|
||||
|
||||
@@ -13,7 +14,7 @@ CADCoreLike = Union[TopoDS_Shape, TopLoc_Location] # Faces, Edges, Vertices and
|
||||
CADLike = Union[CADCoreLike, any] # build123d and cadquery types
|
||||
|
||||
|
||||
def get_shape(obj: CADLike, error: bool = True) -> Optional[CADCoreLike]:
|
||||
def get_shape(obj: CADLike, error: bool = True, in_iter: bool = False) -> Optional[CADCoreLike]:
|
||||
""" Get the shape of a CAD-like object """
|
||||
|
||||
# Try to grab a shape if a different type of object was passed
|
||||
@@ -40,6 +41,17 @@ def get_shape(obj: CADLike, error: bool = True) -> Optional[CADCoreLike]:
|
||||
if isinstance(obj, TopoDS_Shape):
|
||||
return obj
|
||||
|
||||
# Handle iterables like Build123d ShapeList by extracting all sub-shapes and making a compound
|
||||
if not in_iter:
|
||||
try:
|
||||
obj_iter = iter(obj)
|
||||
# print(obj, ' -> ', obj_iter)
|
||||
shapes_raw = [get_shape(sub_obj, error=False, in_iter=True) for sub_obj in obj_iter]
|
||||
shapes_bd = [Shape(shape) for shape in shapes_raw if shape is not None]
|
||||
return get_shape(Compound(shapes_bd), error)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
if error:
|
||||
raise ValueError(f'Cannot show object of type {type(obj)} (submit issue?)')
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user