mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
Count features again after changes to model and support for sending arbitrary lists of shapes as a single model.
This commit is contained in:
@@ -43,10 +43,11 @@ const emit = defineEmits<{ remove: [] }>()
|
|||||||
|
|
||||||
let modelName = props.meshes[0].getExtras()[extrasNameKey] // + " blah blah blah blah blag blah blah blah"
|
let modelName = props.meshes[0].getExtras()[extrasNameKey] // + " blah blah blah blah blag blah blah blah"
|
||||||
|
|
||||||
// Reactive properties
|
// Count the number of faces, edges and vertices
|
||||||
const enabledFeatures = defineModel<Array<number>>("enabledFeatures", {default: [0, 1, 2]});
|
let faceCount = ref(-1);
|
||||||
const opacity = defineModel<number>("opacity", {default: 1});
|
let edgeCount = ref(-1);
|
||||||
const wireframe = ref(false);
|
let vertexCount = ref(-1);
|
||||||
|
|
||||||
// Clipping planes are handled in y-up space (swapped on interface, Z inverted later)
|
// Clipping planes are handled in y-up space (swapped on interface, Z inverted later)
|
||||||
const clipPlaneX = ref(1);
|
const clipPlaneX = ref(1);
|
||||||
const clipPlaneSwappedX = ref(false);
|
const clipPlaneSwappedX = ref(false);
|
||||||
@@ -56,24 +57,10 @@ const clipPlaneZ = ref(1);
|
|||||||
const clipPlaneSwappedZ = ref(false);
|
const clipPlaneSwappedZ = ref(false);
|
||||||
const edgeWidth = ref(settings.edgeWidth);
|
const edgeWidth = ref(settings.edgeWidth);
|
||||||
|
|
||||||
// Count the number of faces, edges and vertices
|
// Misc properties
|
||||||
let faceCount = props.meshes
|
const enabledFeatures = defineModel<Array<number>>("enabledFeatures", {default: [0, 1, 2]});
|
||||||
.flatMap((m) => m.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.TRIANGLES))
|
const opacity = defineModel<number>("opacity", {default: 1});
|
||||||
.map(p => (p.getExtras()?.face_triangles_end as any)?.length ?? 1)
|
const wireframe = ref(false);
|
||||||
.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)
|
|
||||||
|
|
||||||
// Listeners for changes in the properties (or viewer reloads)
|
// Listeners for changes in the properties (or viewer reloads)
|
||||||
function onEnabledFeaturesChange(newEnabledFeatures: Array<number>) {
|
function onEnabledFeaturesChange(newEnabledFeatures: Array<number>) {
|
||||||
@@ -81,9 +68,6 @@ function onEnabledFeaturesChange(newEnabledFeatures: Array<number>) {
|
|||||||
let scene = props.viewer?.scene;
|
let scene = props.viewer?.scene;
|
||||||
let sceneModel = (scene as any)?._model;
|
let sceneModel = (scene as any)?._model;
|
||||||
if (!scene || !sceneModel) return;
|
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) => {
|
sceneModel.traverse((child: MObject3D) => {
|
||||||
if (child.userData[extrasNameKey] === modelName) {
|
if (child.userData[extrasNameKey] === modelName) {
|
||||||
let childIsFace = child.type == 'Mesh' || child.type == 'SkinnedMesh'
|
let childIsFace = child.type == 'Mesh' || child.type == 'SkinnedMesh'
|
||||||
@@ -107,10 +91,6 @@ function onOpacityChange(newOpacity: number) {
|
|||||||
let scene = props.viewer?.scene;
|
let scene = props.viewer?.scene;
|
||||||
let sceneModel = (scene as any)?._model;
|
let sceneModel = (scene as any)?._model;
|
||||||
if (!scene || !sceneModel) return;
|
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) => {
|
sceneModel.traverse((child: MObject3D) => {
|
||||||
if (child.userData[extrasNameKey] === modelName) {
|
if (child.userData[extrasNameKey] === modelName) {
|
||||||
if (child.material && child.material.opacity !== newOpacity) {
|
if (child.material && child.material.opacity !== newOpacity) {
|
||||||
@@ -129,10 +109,6 @@ function onWireframeChange(newWireframe: boolean) {
|
|||||||
let scene = props.viewer?.scene;
|
let scene = props.viewer?.scene;
|
||||||
let sceneModel = (scene as any)?._model;
|
let sceneModel = (scene as any)?._model;
|
||||||
if (!scene || !sceneModel) return;
|
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) => {
|
sceneModel.traverse((child: MObject3D) => {
|
||||||
if (child.userData[extrasNameKey] === modelName) {
|
if (child.userData[extrasNameKey] === modelName) {
|
||||||
if (child.material && child.material.wireframe !== newWireframe) {
|
if (child.material && child.material.wireframe !== newWireframe) {
|
||||||
@@ -268,9 +244,8 @@ function onModelLoad() {
|
|||||||
let scene = props.viewer?.scene;
|
let scene = props.viewer?.scene;
|
||||||
let sceneModel = (scene as any)?._model;
|
let sceneModel = (scene as any)?._model;
|
||||||
if (!scene || !sceneModel) return;
|
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
|
// Add darkened back faces for all face objects to improve cutting planes
|
||||||
// of not actually removing the primitives from the scene graph
|
|
||||||
let childrenToAdd: Array<MObject3D> = [];
|
let childrenToAdd: Array<MObject3D> = [];
|
||||||
sceneModel.traverse((child: MObject3D) => {
|
sceneModel.traverse((child: MObject3D) => {
|
||||||
if (child.userData[extrasNameKey] === modelName) {
|
if (child.userData[extrasNameKey] === modelName) {
|
||||||
@@ -303,6 +278,28 @@ function onModelLoad() {
|
|||||||
});
|
});
|
||||||
childrenToAdd.forEach((child: MObject3D) => sceneModel.add(child));
|
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...
|
// Furthermore...
|
||||||
// Enabled features may have been reset after a reload
|
// Enabled features may have been reset after a reload
|
||||||
onEnabledFeaturesChange(enabledFeatures.value)
|
onEnabledFeaturesChange(enabledFeatures.value)
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
Utilities to work with CAD objects
|
Utilities to work with CAD objects
|
||||||
"""
|
"""
|
||||||
import hashlib
|
import hashlib
|
||||||
from typing import Optional, Union, List, Tuple
|
from typing import Optional, Union, Tuple
|
||||||
|
|
||||||
from OCP.TopLoc import TopLoc_Location
|
from OCP.TopLoc import TopLoc_Location
|
||||||
from OCP.TopoDS import TopoDS_Shape
|
from OCP.TopoDS import TopoDS_Shape
|
||||||
|
from build123d import Compound, Shape
|
||||||
|
|
||||||
from yacv_server.gltf import GLTFMgr
|
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
|
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 """
|
""" Get the shape of a CAD-like object """
|
||||||
|
|
||||||
# Try to grab a shape if a different type of object was passed
|
# 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):
|
if isinstance(obj, TopoDS_Shape):
|
||||||
return obj
|
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:
|
if error:
|
||||||
raise ValueError(f'Cannot show object of type {type(obj)} (submit issue?)')
|
raise ValueError(f'Cannot show object of type {type(obj)} (submit issue?)')
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user