See #1227. Recalculate normals and limited dissolve when layerset slicing and generating underside clipping geom.

This commit is contained in:
Dion Moult
2025-03-16 10:56:18 +11:00
parent b2e2f765ca
commit 2941a4ad1a
2 changed files with 16 additions and 26 deletions
+12 -21
View File
@@ -20,7 +20,6 @@ from __future__ import annotations
import os import os
import re import re
import bpy import bpy
import math
import bmesh import bmesh
import logging import logging
import ifcopenshell.geom import ifcopenshell.geom
@@ -36,6 +35,7 @@ import bonsai.bim.import_ifc
import numpy as np import numpy as np
import numpy.typing as npt import numpy.typing as npt
from ifcopenshell.util.shape_builder import np_to_4d from ifcopenshell.util.shape_builder import np_to_4d
from math import atan, radians
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from pathlib import Path from pathlib import Path
from typing import Union, Any, Optional from typing import Union, Any, Optional
@@ -857,9 +857,9 @@ class Loader(bonsai.core.tool.Loader):
camera.BIMCameraProperties.height = height camera.BIMCameraProperties.height = height
if width > height: if width > height:
fov = 2 * math.atan(width / (2 * abs_min_z)) fov = 2 * atan(width / (2 * abs_min_z))
else: else:
fov = 2 * math.atan(height / (2 * abs_min_z)) fov = 2 * atan(height / (2 * abs_min_z))
camera.angle = fov camera.angle = fov
@@ -1060,13 +1060,15 @@ class Loader(bonsai.core.tool.Loader):
for i, material in enumerate(mesh.materials): for i, material in enumerate(mesh.materials):
if style := tool.Ifc.get_entity(material): if style := tool.Ifc.get_entity(material):
styles[style] = i styles[style] = i
for layer in layer_set.MaterialLayers[:-1]: last_i = len(layer_set.MaterialLayers) - 1
for i, layer in enumerate(layer_set.MaterialLayers):
prev_co = co.copy() prev_co = co.copy()
co += no * layer.LayerThickness * cls.unit_scale co += no * layer.LayerThickness * cls.unit_scale
bisect_geom = bmesh.ops.bisect_plane( if i != last_i:
bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no bisect_geom = bmesh.ops.bisect_plane(
) bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no
bmesh.ops.duplicate(bm, geom=bisect_geom["geom_cut"]) )
bmesh.ops.duplicate(bm, geom=bisect_geom["geom_cut"])
if style := ifcopenshell.util.representation.get_material_style(layer.Material, body): if style := ifcopenshell.util.representation.get_material_style(layer.Material, body):
if (material_index := styles.get(style, None)) is None: if (material_index := styles.get(style, None)) is None:
material_index = len(mesh.materials) material_index = len(mesh.materials)
@@ -1078,19 +1080,8 @@ class Loader(bonsai.core.tool.Loader):
face.material_index = material_index face.material_index = material_index
has_layer_styles = True has_layer_styles = True
# Last layer bmesh.ops.dissolve_limit(bm, angle_limit=radians(1), verts=bm.verts, edges=bm.edges, delimit={"MATERIAL"})
layer = layer_set.MaterialLayers[-1] bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
if style := ifcopenshell.util.representation.get_material_style(layer.Material, body):
if (material_index := styles.get(style, None)) is None:
material_index = len(mesh.materials)
mesh.materials.append(tool.Ifc.get_object(style))
for face in bisect_geom["geom"]:
if isinstance(face, bmesh.types.BMFace):
center = face.calc_center_median()
# if center.y > co.y:
if (center - co).dot(no) >= 0:
face.material_index = material_index
has_layer_styles = True
bm.to_mesh(mesh) bm.to_mesh(mesh)
bm.free() bm.free()
+4 -5
View File
@@ -38,15 +38,12 @@ import ifcopenshell.util.unit
import bonsai.core.geometry import bonsai.core.geometry
import bonsai.core.tool import bonsai.core.tool
import bonsai.tool as tool import bonsai.tool as tool
import bonsai.core.geometry as geometry from math import atan, cos, degrees, pi, radians
from math import atan, cos, degrees, pi, inf
from mathutils import Matrix, Vector from mathutils import Matrix, Vector
from copy import deepcopy from copy import deepcopy
from functools import partial from functools import partial
from bonsai.bim import import_ifc from bonsai.bim import import_ifc
# TODO: This line is somehow keeping the world from falling apart with a circular import error.
from bonsai.bim.module.geometry.helper import Helper
from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData
from bonsai.bim.module.model.opening import FilledOpeningGenerator from bonsai.bim.module.model.opening import FilledOpeningGenerator
from ifcopenshell.util.shape_builder import ShapeBuilder from ifcopenshell.util.shape_builder import ShapeBuilder
@@ -2108,6 +2105,7 @@ class Model(bonsai.core.tool.Model):
bm = bmesh.new() bm = bmesh.new()
bm.from_mesh(obj.data) bm.from_mesh(obj.data)
bmesh.ops.dissolve_limit(bm, angle_limit=radians(1), verts=bm.verts, edges=bm.edges)
bm.faces.ensure_lookup_table() bm.faces.ensure_lookup_table()
clipping_bm = bmesh.new() clipping_bm = bmesh.new()
@@ -2117,7 +2115,7 @@ class Model(bonsai.core.tool.Model):
face.normal_update() face.normal_update()
normal = face.normal.to_4d() normal = face.normal.to_4d()
normal.w = 0 normal.w = 0
if (obj.matrix_world @ normal).z >= 0: if (obj.matrix_world @ normal).z >= -0.5:
continue continue
new_verts = [] new_verts = []
for vert in face.verts: for vert in face.verts:
@@ -2130,6 +2128,7 @@ class Model(bonsai.core.tool.Model):
if not len(clipping_bm.faces): if not len(clipping_bm.faces):
return return
bmesh.ops.recalc_face_normals(clipping_bm, faces=clipping_bm.faces)
return clipping_bm # clipping_bm is in project units return clipping_bm # clipping_bm is in project units
@classmethod @classmethod