mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 10:11:46 +00:00
shape_builder - remove mathutils dependency completely #5192
This commit is contained in:
@@ -27,7 +27,6 @@ import ifcopenshell.util.element
|
|||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from ifcopenshell.util.shape_builder import V
|
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import bonsai.core.geometry
|
import bonsai.core.geometry
|
||||||
import bonsai.core.geometry as core
|
import bonsai.core.geometry as core
|
||||||
@@ -40,6 +39,8 @@ from typing import Union, Any, Optional
|
|||||||
import json
|
import json
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
|
V_ = tool.Blender.V_
|
||||||
|
|
||||||
|
|
||||||
def update_door_modifier_representation(context: bpy.types.Context) -> None:
|
def update_door_modifier_representation(context: bpy.types.Context) -> None:
|
||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
@@ -188,15 +189,15 @@ def bm_sort_out_geom(
|
|||||||
def bm_mirror(
|
def bm_mirror(
|
||||||
bm: bmesh.types.BMesh,
|
bm: bmesh.types.BMesh,
|
||||||
verts: list[bmesh.types.BMVert],
|
verts: list[bmesh.types.BMVert],
|
||||||
mirror_axes: Vector = V(1, 0, 0).freeze(),
|
mirror_axes: Vector = V_(1, 0, 0).freeze(),
|
||||||
mirror_point: Vector = V(0, 0, 0).freeze(),
|
mirror_point: Vector = V_(0, 0, 0).freeze(),
|
||||||
create_copy: bool = False,
|
create_copy: bool = False,
|
||||||
) -> list[bmesh.types.BMVert]:
|
) -> list[bmesh.types.BMVert]:
|
||||||
matrix = Matrix.Translation(mirror_point)
|
matrix = Matrix.Translation(mirror_point)
|
||||||
for i, v in enumerate(mirror_axes):
|
for i, v in enumerate(mirror_axes):
|
||||||
if not v:
|
if not v:
|
||||||
continue
|
continue
|
||||||
mirror_axis = V(0, 0, 0)
|
mirror_axis = V_(0, 0, 0)
|
||||||
mirror_axis[i] = 1.0
|
mirror_axis[i] = 1.0
|
||||||
matrix = matrix @ Matrix.Scale(-1, 4, mirror_axis)
|
matrix = matrix @ Matrix.Scale(-1, 4, mirror_axis)
|
||||||
matrix = matrix @ Matrix.Translation(-mirror_point)
|
matrix = matrix @ Matrix.Translation(-mirror_point)
|
||||||
@@ -219,9 +220,9 @@ def create_bm_extruded_profile(
|
|||||||
points: list[Vector],
|
points: list[Vector],
|
||||||
edges: Optional[list[tuple[int, int]]] = None,
|
edges: Optional[list[tuple[int, int]]] = None,
|
||||||
faces: Optional[list[list[int]]] = None,
|
faces: Optional[list[list[int]]] = None,
|
||||||
position: Vector = V(0, 0, 0).freeze(),
|
position: Vector = V_(0, 0, 0).freeze(),
|
||||||
magnitude: float = 1.0,
|
magnitude: float = 1.0,
|
||||||
extrusion_vector: Vector = V(0, 0, 1).freeze(),
|
extrusion_vector: Vector = V_(0, 0, 1).freeze(),
|
||||||
) -> list[bmesh.types.BMVert]:
|
) -> list[bmesh.types.BMVert]:
|
||||||
bm.verts.index_update()
|
bm.verts.index_update()
|
||||||
bm.edges.index_update()
|
bm.edges.index_update()
|
||||||
@@ -250,7 +251,7 @@ def create_bm_extruded_profile(
|
|||||||
|
|
||||||
|
|
||||||
def create_bm_door_lining(
|
def create_bm_door_lining(
|
||||||
bm: bmesh.types.BMesh, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
|
bm: bmesh.types.BMesh, size: Vector, thickness: list, position: Vector = V_(0, 0, 0).freeze()
|
||||||
) -> list[bmesh.types.BMVert]:
|
) -> list[bmesh.types.BMVert]:
|
||||||
"""`thickness` of the profile is defined as list in the following order: `(SIDE, TOP)`
|
"""`thickness` of the profile is defined as list in the following order: `(SIDE, TOP)`
|
||||||
|
|
||||||
@@ -363,9 +364,9 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
glass_thickness = 0.01
|
glass_thickness = 0.01
|
||||||
|
|
||||||
# handle dimensions (hardcoded)
|
# handle dimensions (hardcoded)
|
||||||
handle_size = V(120, 40, 20) * 0.001
|
handle_size = V_(120, 40, 20) * 0.001
|
||||||
handle_offset = V(60, 0, 1000) * 0.001 # to the handle center
|
handle_offset = V_(60, 0, 1000) * 0.001 # to the handle center
|
||||||
handle_center_offset = V(handle_size.y / 2, 0, handle_size.z) / 2
|
handle_center_offset = V_(handle_size.y / 2, 0, handle_size.z) / 2
|
||||||
|
|
||||||
if transfom_offset:
|
if transfom_offset:
|
||||||
panel_height = transfom_offset + transom_thickness - lining_to_panel_offset_x - threshold_thickness
|
panel_height = transfom_offset + transom_thickness - lining_to_panel_offset_x - threshold_thickness
|
||||||
@@ -377,7 +378,7 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
|
|
||||||
# add lining
|
# add lining
|
||||||
lining_size = V(overall_width, lining_depth, lining_height)
|
lining_size = V_(overall_width, lining_depth, lining_height)
|
||||||
lining_thickness = [side_lining_thickness, top_lining_thickness]
|
lining_thickness = [side_lining_thickness, top_lining_thickness]
|
||||||
lining_verts = create_bm_door_lining(bm, lining_size, lining_thickness)
|
lining_verts = create_bm_door_lining(bm, lining_size, lining_thickness)
|
||||||
|
|
||||||
@@ -385,8 +386,8 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
if not threshold_thickness:
|
if not threshold_thickness:
|
||||||
threshold_verts = []
|
threshold_verts = []
|
||||||
else:
|
else:
|
||||||
threshold_size = V(threshold_width, threshold_depth, threshold_thickness)
|
threshold_size = V_(threshold_width, threshold_depth, threshold_thickness)
|
||||||
threshold_position = V(side_lining_thickness, threshold_offset, 0)
|
threshold_position = V_(side_lining_thickness, threshold_offset, 0)
|
||||||
threshold_verts = create_bm_box(bm, threshold_size, threshold_position)
|
threshold_verts = create_bm_box(bm, threshold_size, threshold_position)
|
||||||
|
|
||||||
# add casings
|
# add casings
|
||||||
@@ -400,12 +401,12 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
]
|
]
|
||||||
outer_casing_thickness = inner_casing_thickness.copy() if double_swing_door else casing_thickness
|
outer_casing_thickness = inner_casing_thickness.copy() if double_swing_door else casing_thickness
|
||||||
|
|
||||||
casing_size = V(overall_width + casing_wall_overlap * 2, casing_depth, overall_height + casing_wall_overlap)
|
casing_size = V_(overall_width + casing_wall_overlap * 2, casing_depth, overall_height + casing_wall_overlap)
|
||||||
casing_position = V(-casing_wall_overlap, -casing_depth, 0)
|
casing_position = V_(-casing_wall_overlap, -casing_depth, 0)
|
||||||
outer_casing_verts = create_bm_door_lining(bm, casing_size, outer_casing_thickness, casing_position)
|
outer_casing_verts = create_bm_door_lining(bm, casing_size, outer_casing_thickness, casing_position)
|
||||||
casing_verts.extend(outer_casing_verts)
|
casing_verts.extend(outer_casing_verts)
|
||||||
|
|
||||||
inner_casing_position = V(-casing_wall_overlap, lining_depth, 0)
|
inner_casing_position = V_(-casing_wall_overlap, lining_depth, 0)
|
||||||
inner_casing_verts = create_bm_door_lining(bm, casing_size, inner_casing_thickness, inner_casing_position)
|
inner_casing_verts = create_bm_door_lining(bm, casing_size, inner_casing_thickness, inner_casing_position)
|
||||||
casing_verts.extend(inner_casing_verts)
|
casing_verts.extend(inner_casing_verts)
|
||||||
|
|
||||||
@@ -417,12 +418,12 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
door_verts.extend(create_bm_box(bm, panel_size, panel_position))
|
door_verts.extend(create_bm_box(bm, panel_size, panel_position))
|
||||||
# add door handle
|
# add door handle
|
||||||
handle_points = [
|
handle_points = [
|
||||||
V(0, 0, 0),
|
V_(0, 0, 0),
|
||||||
V(0, -handle_size.y, 0),
|
V_(0, -handle_size.y, 0),
|
||||||
V(handle_size.x, -handle_size.y, 0),
|
V_(handle_size.x, -handle_size.y, 0),
|
||||||
V(handle_size.x, -handle_size.y / 2, 0),
|
V_(handle_size.x, -handle_size.y / 2, 0),
|
||||||
V(handle_size.y / 2, -handle_size.y / 2, 0),
|
V_(handle_size.y / 2, -handle_size.y / 2, 0),
|
||||||
V(handle_size.y / 2, 0, 0),
|
V_(handle_size.y / 2, 0, 0),
|
||||||
]
|
]
|
||||||
handle_position = panel_position + handle_offset - handle_center_offset
|
handle_position = panel_position + handle_offset - handle_center_offset
|
||||||
door_handle_verts = create_bm_extruded_profile(
|
door_handle_verts = create_bm_extruded_profile(
|
||||||
@@ -432,22 +433,22 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
|
|
||||||
if door_swing_type == "LEFT":
|
if door_swing_type == "LEFT":
|
||||||
bm_mirror(
|
bm_mirror(
|
||||||
bm, door_handle_verts, mirror_axes=V(1, 0, 0), mirror_point=panel_position + V(panel_size.x / 2, 0, 0)
|
bm, door_handle_verts, mirror_axes=V_(1, 0, 0), mirror_point=panel_position + V_(panel_size.x / 2, 0, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
door_handle_mirrored_verts = bm_mirror(
|
door_handle_mirrored_verts = bm_mirror(
|
||||||
bm,
|
bm,
|
||||||
door_handle_verts,
|
door_handle_verts,
|
||||||
mirror_axes=V(0, 1, 0),
|
mirror_axes=V_(0, 1, 0),
|
||||||
mirror_point=handle_position + V(0, panel_size.y / 2, 0),
|
mirror_point=handle_position + V_(0, panel_size.y / 2, 0),
|
||||||
create_copy=True,
|
create_copy=True,
|
||||||
)
|
)
|
||||||
door_verts.extend(door_handle_mirrored_verts)
|
door_verts.extend(door_handle_mirrored_verts)
|
||||||
return door_verts
|
return door_verts
|
||||||
|
|
||||||
door_verts = []
|
door_verts = []
|
||||||
panel_size = V(panel_width, panel_depth, panel_height)
|
panel_size = V_(panel_width, panel_depth, panel_height)
|
||||||
panel_position = V(lining_to_panel_offset_x, lining_to_panel_offset_y, threshold_thickness)
|
panel_position = V_(lining_to_panel_offset_x, lining_to_panel_offset_y, threshold_thickness)
|
||||||
|
|
||||||
if double_door:
|
if double_door:
|
||||||
# keeping a little space between doors for readibility
|
# keeping a little space between doors for readibility
|
||||||
@@ -455,8 +456,8 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
panel_size.x = panel_size.x / 2 - double_door_offset
|
panel_size.x = panel_size.x / 2 - double_door_offset
|
||||||
door_verts.extend(create_bm_door_panel(panel_size, panel_position, "LEFT"))
|
door_verts.extend(create_bm_door_panel(panel_size, panel_position, "LEFT"))
|
||||||
|
|
||||||
mirror_point = panel_position + V(door_opening_width / 2, 0, 0)
|
mirror_point = panel_position + V_(door_opening_width / 2, 0, 0)
|
||||||
door_verts.extend(bm_mirror(bm, door_verts, V(1, 0, 0), mirror_point, create_copy=True))
|
door_verts.extend(bm_mirror(bm, door_verts, V_(1, 0, 0), mirror_point, create_copy=True))
|
||||||
else:
|
else:
|
||||||
door_swing_type = "LEFT" if door_type.endswith("LEFT") else "RIGHT"
|
door_swing_type = "LEFT" if door_type.endswith("LEFT") else "RIGHT"
|
||||||
door_verts.extend(create_bm_door_panel(panel_size, panel_position, door_swing_type))
|
door_verts.extend(create_bm_door_panel(panel_size, panel_position, door_swing_type))
|
||||||
@@ -474,9 +475,9 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
transom_thickness,
|
transom_thickness,
|
||||||
]
|
]
|
||||||
window_lining_thickness.append(transom_thickness)
|
window_lining_thickness.append(transom_thickness)
|
||||||
window_lining_size = V(overall_width, lining_depth, window_lining_height)
|
window_lining_size = V_(overall_width, lining_depth, window_lining_height)
|
||||||
window_position = V(0, 0, overall_height - window_lining_height)
|
window_position = V_(0, 0, overall_height - window_lining_height)
|
||||||
frame_size = V(door_opening_width, frame_depth, frame_height)
|
frame_size = V_(door_opening_width, frame_depth, frame_height)
|
||||||
window_lining_verts, frame_verts, glass_verts = create_bm_window(
|
window_lining_verts, frame_verts, glass_verts = create_bm_window(
|
||||||
bm,
|
bm,
|
||||||
window_lining_size,
|
window_lining_size,
|
||||||
@@ -490,7 +491,7 @@ def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
lining_offset_verts = lining_verts + door_verts + window_lining_verts + frame_verts + glass_verts
|
lining_offset_verts = lining_verts + door_verts + window_lining_verts + frame_verts + glass_verts
|
||||||
bmesh.ops.translate(bm, vec=V(0, lining_offset, 0), verts=lining_offset_verts)
|
bmesh.ops.translate(bm, vec=V_(0, lining_offset, 0), verts=lining_offset_verts)
|
||||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
|
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
|
||||||
|
|
||||||
if bpy.context.active_object.mode == "EDIT":
|
if bpy.context.active_object.mode == "EDIT":
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import ifcopenshell
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from ifcopenshell.util.shape_builder import V
|
|
||||||
import bonsai.core.root
|
import bonsai.core.root
|
||||||
import bonsai.core.geometry
|
import bonsai.core.geometry
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
@@ -125,6 +124,7 @@ def update_railing_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
"""
|
"""
|
||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
props = obj.BIMRailingProperties
|
props = obj.BIMRailingProperties
|
||||||
|
V_ = tool.Blender.V_
|
||||||
|
|
||||||
# NOTE: using Data since bmesh update will hapen very often
|
# NOTE: using Data since bmesh update will hapen very often
|
||||||
if not RailingData.is_loaded:
|
if not RailingData.is_loaded:
|
||||||
@@ -171,8 +171,8 @@ def update_railing_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
v0, v1 = main_edge.verts
|
v0, v1 = main_edge.verts
|
||||||
edge_dissolving_verts.extend([v0, v1])
|
edge_dissolving_verts.extend([v0, v1])
|
||||||
|
|
||||||
edge_dir = ((v1.co - v0.co) * V(1, 1, 0)).normalized()
|
edge_dir = ((v1.co - v0.co) * V_(1, 1, 0)).normalized()
|
||||||
ortho_vector = edge_dir.cross(V(0, 0, 1))
|
ortho_vector = edge_dir.cross(V_(0, 0, 1))
|
||||||
|
|
||||||
extruded_geom = bmesh.ops.extrude_edge_only(bm, edges=[main_edge])["geom"]
|
extruded_geom = bmesh.ops.extrude_edge_only(bm, edges=[main_edge])["geom"]
|
||||||
extruded_verts = bm_sort_out_geom(extruded_geom)["verts"]
|
extruded_verts = bm_sort_out_geom(extruded_geom)["verts"]
|
||||||
|
|||||||
@@ -31,11 +31,12 @@ import ifcopenshell.util.element
|
|||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
import ifcopenshell.util.shape_builder
|
import ifcopenshell.util.shape_builder
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from ifcopenshell.util.shape_builder import V
|
|
||||||
from bmesh.types import BMVert
|
from bmesh.types import BMVert
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
V_ = tool.Blender.V_
|
||||||
|
|
||||||
|
|
||||||
def update_window_modifier_representation(context: bpy.types.Context) -> None:
|
def update_window_modifier_representation(context: bpy.types.Context) -> None:
|
||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
@@ -131,7 +132,7 @@ def update_window_modifier_representation(context: bpy.types.Context) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def create_bm_window_frame(
|
def create_bm_window_frame(
|
||||||
bm: bmesh.types.BMesh, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
|
bm: bmesh.types.BMesh, size: Vector, thickness: list, position: Vector = V_(0, 0, 0).freeze()
|
||||||
) -> list[bmesh.types.BMVert]:
|
) -> list[bmesh.types.BMVert]:
|
||||||
"""`thickness` of the profile is defined as list in the following order:
|
"""`thickness` of the profile is defined as list in the following order:
|
||||||
`(LEFT, TOP, RIGHT, BOTTOM)`
|
`(LEFT, TOP, RIGHT, BOTTOM)`
|
||||||
@@ -198,7 +199,7 @@ def create_bm_window_frame(
|
|||||||
|
|
||||||
|
|
||||||
def create_bm_box(
|
def create_bm_box(
|
||||||
bm: bmesh.types.BMesh, size: Vector = V(1, 1, 1).freeze(), position: Vector = V(0, 0, 0).freeze()
|
bm: bmesh.types.BMesh, size: Vector = V_(1, 1, 1).freeze(), position: Vector = V_(0, 0, 0).freeze()
|
||||||
) -> list[bmesh.types.BMVert]:
|
) -> list[bmesh.types.BMVert]:
|
||||||
"""create a box of `size`, position box first vertex at `position`"""
|
"""create a box of `size`, position box first vertex at `position`"""
|
||||||
box_verts = bmesh.ops.create_cube(bm, size=1)["verts"]
|
box_verts = bmesh.ops.create_cube(bm, size=1)["verts"]
|
||||||
@@ -230,13 +231,13 @@ def create_bm_window(
|
|||||||
window_lining_verts = create_bm_window_frame(bm, lining_size, lining_thickness)
|
window_lining_verts = create_bm_window_frame(bm, lining_size, lining_thickness)
|
||||||
|
|
||||||
# window frame
|
# window frame
|
||||||
frame_position = V(x_offsets[0], lining_to_panel_offset_y_full, x_offsets[3])
|
frame_position = V_(x_offsets[0], lining_to_panel_offset_y_full, x_offsets[3])
|
||||||
frame_verts = create_bm_window_frame(bm, frame_size, frame_thickness, frame_position)
|
frame_verts = create_bm_window_frame(bm, frame_size, frame_thickness, frame_position)
|
||||||
|
|
||||||
# window glass
|
# window glass
|
||||||
glass_size = frame_size - V(frame_thickness * 2, 0, frame_thickness * 2)
|
glass_size = frame_size - V_(frame_thickness * 2, 0, frame_thickness * 2)
|
||||||
glass_size.y = glass_thickness
|
glass_size.y = glass_thickness
|
||||||
glass_position = frame_position + V(frame_thickness, frame_size.y / 2 - glass_thickness / 2, frame_thickness)
|
glass_position = frame_position + V_(frame_thickness, frame_size.y / 2 - glass_thickness / 2, frame_thickness)
|
||||||
|
|
||||||
glass_verts = create_bm_box(bm, glass_size, glass_position)
|
glass_verts = create_bm_box(bm, glass_size, glass_position)
|
||||||
|
|
||||||
@@ -324,7 +325,7 @@ def update_window_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
frame_thickness = props.frame_thickness[panel_i]
|
frame_thickness = props.frame_thickness[panel_i]
|
||||||
lining_to_panel_offset_y_full = (lining_depth - frame_depth) + lining_to_panel_offset_y
|
lining_to_panel_offset_y_full = (lining_depth - frame_depth) + lining_to_panel_offset_y
|
||||||
# add window
|
# add window
|
||||||
window_lining_size = V(
|
window_lining_size = V_(
|
||||||
panel_width,
|
panel_width,
|
||||||
lining_depth,
|
lining_depth,
|
||||||
panel_height,
|
panel_height,
|
||||||
@@ -358,7 +359,7 @@ def update_window_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
frame_size.x -= x_offsets[0] + x_offsets[2]
|
frame_size.x -= x_offsets[0] + x_offsets[2]
|
||||||
frame_size.z -= x_offsets[1] + x_offsets[3]
|
frame_size.z -= x_offsets[1] + x_offsets[3]
|
||||||
|
|
||||||
window_position = V(accumulated_width, 0, accumulated_height[column_i])
|
window_position = V_(accumulated_width, 0, accumulated_height[column_i])
|
||||||
lining_verts, panel_verts, glass_verts = create_bm_window(
|
lining_verts, panel_verts, glass_verts = create_bm_window(
|
||||||
bm,
|
bm,
|
||||||
window_lining_size,
|
window_lining_size,
|
||||||
@@ -377,7 +378,7 @@ def update_window_modifier_bmesh(context: bpy.types.Context) -> None:
|
|||||||
accumulated_height[column_i] += panel_height
|
accumulated_height[column_i] += panel_height
|
||||||
accumulated_width += panel_width
|
accumulated_width += panel_width
|
||||||
|
|
||||||
bmesh.ops.translate(bm, vec=V(0, lining_offset, 0), verts=bm.verts)
|
bmesh.ops.translate(bm, vec=V_(0, lining_offset, 0), verts=bm.verts)
|
||||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
|
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
|
||||||
|
|
||||||
if bpy.context.active_object.mode == "EDIT":
|
if bpy.context.active_object.mode == "EDIT":
|
||||||
|
|||||||
@@ -1295,3 +1295,8 @@ class Blender(bonsai.core.tool.Blender):
|
|||||||
if len(blender_binary_path.parents) > 3 and blender_binary_path.parents[2].name == "WindowsApps":
|
if len(blender_binary_path.parents) > 3 and blender_binary_path.parents[2].name == "WindowsApps":
|
||||||
return blender_binary_path.parents[1].name.rsplit("__", 1)[-1]
|
return blender_binary_path.parents[1].name.rsplit("__", 1)[-1]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def V_(cls, *args: float) -> Vector:
|
||||||
|
"""Just a shortcut for creating mathutils Vector."""
|
||||||
|
return Vector(args)
|
||||||
|
|||||||
@@ -39,13 +39,16 @@ 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.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 V, ShapeBuilder
|
from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||||
from typing import Optional, Union, TypeVar, Any, Iterable, Literal
|
from typing import Optional, Union, TypeVar, Any, Iterable, Literal
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
V_ = tool.Blender.V_
|
||||||
|
|
||||||
|
|
||||||
class Model(bonsai.core.tool.Model):
|
class Model(bonsai.core.tool.Model):
|
||||||
@@ -1122,7 +1125,7 @@ class Model(bonsai.core.tool.Model):
|
|||||||
custom_tread_run = any(run != 0 for run in custom_first_last_tread_run)
|
custom_tread_run = any(run != 0 for run in custom_first_last_tread_run)
|
||||||
nosing_overlap = max(nosing_length, 0)
|
nosing_overlap = max(nosing_length, 0)
|
||||||
nosing_tread_gap = -min(nosing_length, 0)
|
nosing_tread_gap = -min(nosing_length, 0)
|
||||||
nosing_overlap_offset = -V(nosing_overlap, 0)
|
nosing_overlap_offset = -V_(nosing_overlap, 0)
|
||||||
|
|
||||||
def define_generic_stair_treads():
|
def define_generic_stair_treads():
|
||||||
vertices.append(Vector([0, 0]))
|
vertices.append(Vector([0, 0]))
|
||||||
@@ -1134,16 +1137,16 @@ class Model(bonsai.core.tool.Model):
|
|||||||
default_tread_edges = np.array(((0, 1), (1, 2)))
|
default_tread_edges = np.array(((0, 1), (1, 2)))
|
||||||
# horizontal tread line
|
# horizontal tread line
|
||||||
if nosing_overlap == 0:
|
if nosing_overlap == 0:
|
||||||
default_tread_verts = (V(0, tread_rise), V(tread_run, tread_rise))
|
default_tread_verts = (V_(0, tread_rise), V_(tread_run, tread_rise))
|
||||||
elif nosing_depth == 0:
|
elif nosing_depth == 0:
|
||||||
default_tread_verts = (V(-nosing_overlap, tread_rise), V(tread_run, tread_rise))
|
default_tread_verts = (V_(-nosing_overlap, tread_rise), V_(tread_run, tread_rise))
|
||||||
else: # nosing_overlap > 0 nosing_depth > 0
|
else: # nosing_overlap > 0 nosing_depth > 0
|
||||||
# kind of L shape
|
# kind of L shape
|
||||||
default_tread_verts = (
|
default_tread_verts = (
|
||||||
V(0, tread_rise - nosing_depth),
|
V_(0, tread_rise - nosing_depth),
|
||||||
V(-nosing_overlap, tread_rise - nosing_depth),
|
V_(-nosing_overlap, tread_rise - nosing_depth),
|
||||||
V(-nosing_overlap, tread_rise),
|
V_(-nosing_overlap, tread_rise),
|
||||||
V(tread_run, tread_rise),
|
V_(tread_run, tread_rise),
|
||||||
)
|
)
|
||||||
add_edges = ((2, 3), (3, 4))
|
add_edges = ((2, 3), (3, 4))
|
||||||
default_tread_edges = np.concatenate((default_tread_edges, add_edges))
|
default_tread_edges = np.concatenate((default_tread_edges, add_edges))
|
||||||
@@ -1166,7 +1169,7 @@ class Model(bonsai.core.tool.Model):
|
|||||||
return default_tread_offset, default_tread_verts
|
return default_tread_offset, default_tread_verts
|
||||||
|
|
||||||
# treads
|
# treads
|
||||||
current_offset = V(0, 0)
|
current_offset = V_(0, 0)
|
||||||
for i in range(number_of_risers):
|
for i in range(number_of_risers):
|
||||||
last_vert_i = len(vertices) - 1
|
last_vert_i = len(vertices) - 1
|
||||||
tread_offset, tread_verts = get_tread_data(i)
|
tread_offset, tread_verts = get_tread_data(i)
|
||||||
@@ -1178,9 +1181,9 @@ class Model(bonsai.core.tool.Model):
|
|||||||
if stair_type == "WOOD/STEEL":
|
if stair_type == "WOOD/STEEL":
|
||||||
builder = ShapeBuilder(None)
|
builder = ShapeBuilder(None)
|
||||||
# full tread rectangle
|
# full tread rectangle
|
||||||
get_tread_verts = partial(builder.get_rectangle_coords, position=V(0, -(tread_depth - tread_rise)))
|
get_tread_verts = partial(builder.get_rectangle_coords, position=V_(0, -(tread_depth - tread_rise)))
|
||||||
default_tread_verts = get_tread_verts(size=V(tread_run + nosing_overlap, tread_depth))
|
default_tread_verts = get_tread_verts(size=V_(tread_run + nosing_overlap, tread_depth))
|
||||||
default_tread_offset = V(tread_run + nosing_tread_gap, tread_rise)
|
default_tread_offset = V_(tread_run + nosing_tread_gap, tread_rise)
|
||||||
|
|
||||||
def get_tread_data(i):
|
def get_tread_data(i):
|
||||||
if custom_tread_run:
|
if custom_tread_run:
|
||||||
@@ -1193,12 +1196,12 @@ class Model(bonsai.core.tool.Model):
|
|||||||
if current_tread_run:
|
if current_tread_run:
|
||||||
tread_offset = default_tread_offset.copy()
|
tread_offset = default_tread_offset.copy()
|
||||||
tread_offset.x = current_tread_run + nosing_tread_gap
|
tread_offset.x = current_tread_run + nosing_tread_gap
|
||||||
tread_verts = get_tread_verts(size=V(current_tread_run + nosing_overlap, tread_depth))
|
tread_verts = get_tread_verts(size=V_(current_tread_run + nosing_overlap, tread_depth))
|
||||||
return tread_offset, tread_verts
|
return tread_offset, tread_verts
|
||||||
return default_tread_offset, default_tread_verts
|
return default_tread_offset, default_tread_verts
|
||||||
|
|
||||||
# each tread is a separate shape
|
# each tread is a separate shape
|
||||||
cur_offset = V(0, 0)
|
cur_offset = V_(0, 0)
|
||||||
for i in range(number_of_risers):
|
for i in range(number_of_risers):
|
||||||
tread_offset, tread_verts = get_tread_data(i)
|
tread_offset, tread_verts = get_tread_data(i)
|
||||||
cur_trade_shape = [v + cur_offset + nosing_overlap_offset for v in tread_verts]
|
cur_trade_shape = [v + cur_offset + nosing_overlap_offset for v in tread_verts]
|
||||||
@@ -1219,7 +1222,7 @@ class Model(bonsai.core.tool.Model):
|
|||||||
|
|
||||||
# close the shape
|
# close the shape
|
||||||
last_vert_i = len(vertices)
|
last_vert_i = len(vertices)
|
||||||
vertices.append(vertices[-1] * V(1, 0))
|
vertices.append(vertices[-1] * V_(1, 0))
|
||||||
edges.extend([(last_vert_i - 1, last_vert_i), (last_vert_i, 0)])
|
edges.extend([(last_vert_i - 1, last_vert_i), (last_vert_i, 0)])
|
||||||
|
|
||||||
# flip edges direction for ccw polygon winding order
|
# flip edges direction for ccw polygon winding order
|
||||||
@@ -1233,15 +1236,15 @@ class Model(bonsai.core.tool.Model):
|
|||||||
# from the tread diagonal line
|
# from the tread diagonal line
|
||||||
# we're going it define that line, sample it and abrupt it in case it meets a slab
|
# we're going it define that line, sample it and abrupt it in case it meets a slab
|
||||||
# graph: https://www.desmos.com/calculator/bilmnti3cp
|
# graph: https://www.desmos.com/calculator/bilmnti3cp
|
||||||
tread_diagonal_dir = V(tread_run, tread_rise).normalized()
|
tread_diagonal_dir = V_(tread_run, tread_rise).normalized()
|
||||||
# td_vector is clockwise orthogonal vector
|
# td_vector is clockwise orthogonal vector
|
||||||
td_vector = tread_diagonal_dir.yx * V(1, -1) * tread_depth
|
td_vector = tread_diagonal_dir.yx * V_(1, -1) * tread_depth
|
||||||
|
|
||||||
stair_tan = tread_rise / tread_run
|
stair_tan = tread_rise / tread_run
|
||||||
# s0 is just a sampled point from the bottom line
|
# s0 is just a sampled point from the bottom line
|
||||||
# we stick to the third point as the first point
|
# we stick to the third point as the first point
|
||||||
# is affected by customized tread run
|
# is affected by customized tread run
|
||||||
s0 = V(custom_first_last_tread_run[0] or tread_run, tread_rise) + td_vector
|
s0 = V_(custom_first_last_tread_run[0] or tread_run, tread_rise) + td_vector
|
||||||
# comes from y = stair_tan * x + b
|
# comes from y = stair_tan * x + b
|
||||||
b = s0.y - stair_tan * s0.x
|
b = s0.y - stair_tan * s0.x
|
||||||
|
|
||||||
@@ -1250,7 +1253,7 @@ class Model(bonsai.core.tool.Model):
|
|||||||
y = stair_tan * x + b
|
y = stair_tan * x + b
|
||||||
elif x is None:
|
elif x is None:
|
||||||
x = (y - b) / stair_tan
|
x = (y - b) / stair_tan
|
||||||
return V(x, y)
|
return V_(x, y)
|
||||||
|
|
||||||
# top nib
|
# top nib
|
||||||
last_vert = vertices[-1]
|
last_vert = vertices[-1]
|
||||||
|
|||||||
@@ -16,18 +16,24 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
from math import cos, tan, pi
|
from math import cos, tan, pi
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
from ifcopenshell.util.shape_builder import ShapeBuilder, V, np_to_3d, np_normalized
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
GeneratorOutput = Union[list[ifcopenshell.entity_instance], list[list[ifcopenshell.entity_instance]]]
|
||||||
|
|
||||||
|
|
||||||
class LibraryGenerator:
|
class LibraryGenerator:
|
||||||
def generate(self, library_name, output_filename="IFC4 EU Steel.ifc"):
|
def generate(self, library_name, output_filename="IFC4 EU Steel.ifc"):
|
||||||
ifcopenshell.api.pre_listeners = {}
|
ifcopenshell.api.pre_listeners = {}
|
||||||
ifcopenshell.api.post_listeners = {}
|
ifcopenshell.api.post_listeners = {}
|
||||||
|
np_X, np_Y, np_Z = 0, 1, 2
|
||||||
|
|
||||||
self.materials = {}
|
self.materials = {}
|
||||||
|
|
||||||
@@ -65,10 +71,16 @@ class LibraryGenerator:
|
|||||||
|
|
||||||
builder = ShapeBuilder(self.file)
|
builder = ShapeBuilder(self.file)
|
||||||
|
|
||||||
def create_box_objects(width, depth, height, shift_to_center=False, return_representations=False):
|
def create_box_objects(
|
||||||
|
width: float,
|
||||||
|
depth: float,
|
||||||
|
height: float,
|
||||||
|
shift_to_center: bool = False,
|
||||||
|
return_representations: bool = False,
|
||||||
|
) -> Union[list[ifcopenshell.entity_instance], list[list[ifcopenshell.entity_instance]]]:
|
||||||
output = []
|
output = []
|
||||||
|
|
||||||
rectangle = builder.rectangle(size=V(width, depth))
|
rectangle = builder.rectangle(size=(width, depth))
|
||||||
box = builder.extrude(builder.profile(rectangle), height)
|
box = builder.extrude(builder.profile(rectangle), height)
|
||||||
|
|
||||||
if return_representations:
|
if return_representations:
|
||||||
@@ -86,18 +98,20 @@ class LibraryGenerator:
|
|||||||
|
|
||||||
if shift_to_center:
|
if shift_to_center:
|
||||||
shift_to_center = V(-width / 2, -depth / 2)
|
shift_to_center = V(-width / 2, -depth / 2)
|
||||||
builder.translate(output[0], shift_to_center.to_3d())
|
builder.translate(output[0], np_to_3d(shift_to_center))
|
||||||
builder.translate(output[1], shift_to_center)
|
builder.translate(output[1], shift_to_center)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def create_fillet_rectangle(size=None, position=None, fillet_radius=50.0):
|
def create_fillet_rectangle(
|
||||||
|
size: Optional[np.ndarray] = None, position: Optional[np.ndarray] = None, fillet_radius: float = 50.0
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""`fillet_radius` is either float or list of floats for each corner
|
"""`fillet_radius` is either float or list of floats for each corner
|
||||||
in counter-clockwise order starting from bottom left"""
|
in counter-clockwise order starting from bottom left"""
|
||||||
kwargs = dict()
|
kwargs = dict()
|
||||||
if size:
|
if size is not None:
|
||||||
kwargs["size"] = size
|
kwargs["size"] = size
|
||||||
if position:
|
if position is not None:
|
||||||
kwargs["position"] = position
|
kwargs["position"] = position
|
||||||
|
|
||||||
_, _, rectangle = builder.get_simple_2dcurve_data(
|
_, _, rectangle = builder.get_simple_2dcurve_data(
|
||||||
@@ -191,11 +205,13 @@ class LibraryGenerator:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# chairs
|
# chairs
|
||||||
def create_fancy_chair(width, depth, height, seat_level, return_representations=False):
|
def create_fancy_chair(
|
||||||
|
width: float, depth: float, height: float, seat_level: float, return_representations: bool = False
|
||||||
|
) -> GeneratorOutput:
|
||||||
thickness = 50.0
|
thickness = 50.0
|
||||||
shift_to_center = V(-width / 2, 0)
|
shift_to_center = V(-width / 2, 0)
|
||||||
mirror_axis = V(0, 1)
|
mirror_axis = V(0, 1)
|
||||||
output = []
|
output: GeneratorOutput = []
|
||||||
|
|
||||||
items_to_adjust = []
|
items_to_adjust = []
|
||||||
items_3d = []
|
items_3d = []
|
||||||
@@ -292,7 +308,9 @@ class LibraryGenerator:
|
|||||||
"IfcFurnitureType", "Generic Small Bedside Table", representation_3d, representation_2d
|
"IfcFurnitureType", "Generic Small Bedside Table", representation_3d, representation_2d
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_fancy_rectangle_table(width, depth, height, return_representations=False):
|
def create_fancy_rectangle_table(
|
||||||
|
width: float, depth: float, height: float, return_representations=False
|
||||||
|
) -> GeneratorOutput:
|
||||||
output = []
|
output = []
|
||||||
leg_size = 50.0
|
leg_size = 50.0
|
||||||
countertop_thickness = 50.0
|
countertop_thickness = 50.0
|
||||||
@@ -312,7 +330,7 @@ class LibraryGenerator:
|
|||||||
legs_profiles = [builder.profile(leg) for leg in legs_curves]
|
legs_profiles = [builder.profile(leg) for leg in legs_curves]
|
||||||
legs = [builder.extrude(leg, height - countertop_thickness) for leg in legs_profiles]
|
legs = [builder.extrude(leg, height - countertop_thickness) for leg in legs_profiles]
|
||||||
items = [countertop] + legs
|
items = [countertop] + legs
|
||||||
builder.translate(items, shift_to_center.to_3d())
|
builder.translate(items, np_to_3d(shift_to_center))
|
||||||
|
|
||||||
if return_representations:
|
if return_representations:
|
||||||
representation_3d = builder.get_representation(context=self.representations["model_body"], items=items)
|
representation_3d = builder.get_representation(context=self.representations["model_body"], items=items)
|
||||||
@@ -336,17 +354,17 @@ class LibraryGenerator:
|
|||||||
|
|
||||||
# tables
|
# tables
|
||||||
def create_rectangle_table_with_chairs(
|
def create_rectangle_table_with_chairs(
|
||||||
table_width,
|
table_width: float,
|
||||||
table_depth,
|
table_depth: float,
|
||||||
table_height,
|
table_height: float,
|
||||||
chair_width,
|
chair_width: float,
|
||||||
chair_depth,
|
chair_depth: float,
|
||||||
chair_height,
|
chair_height: float,
|
||||||
seat_level,
|
seat_level: float,
|
||||||
table_chair_gap,
|
table_chair_gap: float,
|
||||||
number_of_seats,
|
number_of_seats: int,
|
||||||
side_seats=False,
|
side_seats: bool = False,
|
||||||
):
|
) -> tuple[ifcopenshell.entity_instance, ifcopenshell.entity_instance]:
|
||||||
items_table_3d, items_table_2d = create_fancy_rectangle_table(table_width, table_depth, table_height)
|
items_table_3d, items_table_2d = create_fancy_rectangle_table(table_width, table_depth, table_height)
|
||||||
items_chair_3d, items_chair_2d = create_fancy_chair(chair_width, chair_depth, chair_height, seat_level)
|
items_chair_3d, items_chair_2d = create_fancy_chair(chair_width, chair_depth, chair_height, seat_level)
|
||||||
|
|
||||||
@@ -860,7 +878,7 @@ class LibraryGenerator:
|
|||||||
seat_back = builder.extrude(seat_back, height)
|
seat_back = builder.extrude(seat_back, height)
|
||||||
|
|
||||||
items = armrests + seats + [seat_back]
|
items = armrests + seats + [seat_back]
|
||||||
builder.translate(items, shift_to_center.to_3d())
|
builder.translate(items, np_to_3d(shift_to_center))
|
||||||
# builder.mirror(items, mirror_axis)
|
# builder.mirror(items, mirror_axis)
|
||||||
representation_3d = builder.get_representation(self.representations["model_body"], items=items)
|
representation_3d = builder.get_representation(self.representations["model_body"], items=items)
|
||||||
|
|
||||||
@@ -1103,7 +1121,7 @@ class LibraryGenerator:
|
|||||||
)
|
)
|
||||||
items_3d_to_mirror.append(cistern_3d)
|
items_3d_to_mirror.append(cistern_3d)
|
||||||
|
|
||||||
builder.translate(items_3d, shift_to_center.to_3d())
|
builder.translate(items_3d, np_to_3d(shift_to_center))
|
||||||
|
|
||||||
builder.mirror(items_3d_to_mirror, mirror_axes=mirror_axis)
|
builder.mirror(items_3d_to_mirror, mirror_axes=mirror_axis)
|
||||||
items_3d += items_3d_to_mirror
|
items_3d += items_3d_to_mirror
|
||||||
@@ -1167,7 +1185,7 @@ class LibraryGenerator:
|
|||||||
mask_curve_profile, bottom_mask_height, position=V(0, 0, bottom_height)
|
mask_curve_profile, bottom_mask_height, position=V(0, 0, bottom_height)
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.translate(items_3d_to_center, shift_to_center.to_3d())
|
builder.translate(items_3d_to_center, np_to_3d(shift_to_center))
|
||||||
items_3d = items_3d_to_center + [triangle_bottom_extruded, triangle_bottom_wall_mask]
|
items_3d = items_3d_to_center + [triangle_bottom_extruded, triangle_bottom_wall_mask]
|
||||||
builder.mirror(items_3d, mirror_axis)
|
builder.mirror(items_3d, mirror_axis)
|
||||||
|
|
||||||
@@ -1263,7 +1281,7 @@ class LibraryGenerator:
|
|||||||
basin_downside = builder.extrude(rectangle_second_3d, basin_downside_thickness)
|
basin_downside = builder.extrude(rectangle_second_3d, basin_downside_thickness)
|
||||||
|
|
||||||
items_3d = [main_basin, basin_downside]
|
items_3d = [main_basin, basin_downside]
|
||||||
builder.translate(items_3d, shift_to_center.to_3d())
|
builder.translate(items_3d, np_to_3d(shift_to_center))
|
||||||
representation_3d = builder.get_representation(self.representations["model_body"], items_3d)
|
representation_3d = builder.get_representation(self.representations["model_body"], items_3d)
|
||||||
return representation_3d, representation_2d
|
return representation_3d, representation_2d
|
||||||
|
|
||||||
@@ -1424,19 +1442,21 @@ class LibraryGenerator:
|
|||||||
"IfcFurnitureType", "Neufert Commercial Office Desk and Chair", representation_3d, representation_2d
|
"IfcFurnitureType", "Neufert Commercial Office Desk and Chair", representation_3d, representation_2d
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_dishwasher(width, depth, height):
|
def create_dishwasher(
|
||||||
|
width: float, depth: float, height: float
|
||||||
|
) -> tuple[ifcopenshell.entity_instance, ifcopenshell.entity_instance]:
|
||||||
items_3d, items_2d = create_box_objects(width, depth, height)
|
items_3d, items_2d = create_box_objects(width, depth, height)
|
||||||
circle_first_r = width * 0.1
|
circle_first_r = width * 0.1
|
||||||
circle_second_r = width * 0.3
|
circle_second_r = width * 0.3
|
||||||
center = V(width / 2, depth / 2)
|
center = V(width / 2, depth / 2)
|
||||||
k = center.y / center.x
|
k = center[np_Y] / center[np_X]
|
||||||
|
|
||||||
circle_first = builder.circle(center=center, radius=circle_second_r)
|
circle_first = builder.circle(center=center, radius=circle_second_r)
|
||||||
circle_second = builder.circle(center=center, radius=circle_first_r)
|
circle_second = builder.circle(center=center, radius=circle_first_r)
|
||||||
|
|
||||||
polyline = builder.polyline([(0.0, 0.0), center - center.normalized() * circle_second_r])
|
polyline = builder.polyline([(0.0, 0.0), center - np_normalized(center) * circle_second_r])
|
||||||
mirrored_polylines = builder.mirror(
|
mirrored_polylines = builder.mirror(
|
||||||
polyline, mirror_axes=[V(1, 0), V(0, 1), V(1, 1)], mirror_point=center, create_copy=True
|
polyline, mirror_axes=[(1, 0), (0, 1), (1, 1)], mirror_point=center, create_copy=True
|
||||||
)
|
)
|
||||||
items_2d.extend([circle_first, circle_second, polyline] + mirrored_polylines)
|
items_2d.extend([circle_first, circle_second, polyline] + mirrored_polylines)
|
||||||
|
|
||||||
@@ -1591,13 +1611,13 @@ class LibraryGenerator:
|
|||||||
position=V(rect_offset / 2, rect_offset / 2) + sink_offset,
|
position=V(rect_offset / 2, rect_offset / 2) + sink_offset,
|
||||||
)
|
)
|
||||||
|
|
||||||
circle_position = sink_offset + V((width - sink_offset.x) / 2, depth - rect_offset * 2 - sink_radius)
|
circle_position = sink_offset + V((width - sink_offset[np_X]) / 2, depth - rect_offset * 2 - sink_radius)
|
||||||
sink_circle = builder.circle(circle_position, radius=sink_radius)
|
sink_circle = builder.circle(circle_position, radius=sink_radius)
|
||||||
|
|
||||||
faucet = builder.polyline(
|
faucet = builder.polyline(
|
||||||
(
|
(
|
||||||
((width - sink_offset.x) / 2, faucet_depth),
|
((width - sink_offset[np_X]) / 2, faucet_depth),
|
||||||
((width - sink_offset.x) / 2, faucet_depth - faucet_length),
|
((width - sink_offset[np_X]) / 2, faucet_depth - faucet_length),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
builder.translate(faucet, sink_offset)
|
builder.translate(faucet, sink_offset)
|
||||||
@@ -1626,7 +1646,7 @@ class LibraryGenerator:
|
|||||||
|
|
||||||
sink_table_bottom = builder.extrude(sink_table, height - sink_height)
|
sink_table_bottom = builder.extrude(sink_table, height - sink_height)
|
||||||
items_3d.append(sink_table_bottom)
|
items_3d.append(sink_table_bottom)
|
||||||
builder.translate(items_3d, shift_to_center.to_3d())
|
builder.translate(items_3d, np_to_3d(shift_to_center))
|
||||||
|
|
||||||
representation_2d = builder.get_representation(self.representations["plan_body"], items=items_2d)
|
representation_2d = builder.get_representation(self.representations["plan_body"], items=items_2d)
|
||||||
representation_3d = builder.get_representation(self.representations["model_body"], items=items_3d)
|
representation_3d = builder.get_representation(self.representations["model_body"], items=items_3d)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import bonsai.tool as tool
|
|||||||
from math import cos, sin, tan, pi
|
from math import cos, sin, tan, pi
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from random import uniform
|
from random import uniform
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import ifcopenshell.api
|
|||||||
import boltspy as bolts
|
import boltspy as bolts
|
||||||
from math import cos, pi
|
from math import cos, pi
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
from ifcopenshell.util.shape_builder import ShapeBuilder, V, ifc_safe_vector_type
|
||||||
|
|
||||||
|
|
||||||
class LibraryGenerator:
|
class LibraryGenerator:
|
||||||
@@ -201,20 +201,20 @@ class LibraryGenerator:
|
|||||||
if mode == "LLBB":
|
if mode == "LLBB":
|
||||||
offset = profile.Depth/2 + profiles_gap
|
offset = profile.Depth/2 + profiles_gap
|
||||||
if mirrored:
|
if mirrored:
|
||||||
transform.Axis1 = self.file.createIfcDirection(V(0, 1))
|
transform.Axis1 = self.file.createIfcDirection(ifc_safe_vector_type(V(0, 1)))
|
||||||
transform.Axis2 = self.file.createIfcDirection(V(-1, 0))
|
transform.Axis2 = self.file.createIfcDirection(ifc_safe_vector_type(V(-1, 0)))
|
||||||
transform.LocalOrigin.Coordinates = V(-offset, 0)
|
transform.LocalOrigin.Coordinates = ifc_safe_vector_type(V(-offset, 0))
|
||||||
else:
|
else:
|
||||||
transform.LocalOrigin.Coordinates = V(offset, 0)
|
transform.LocalOrigin.Coordinates = ifc_safe_vector_type(V(offset, 0))
|
||||||
transform.Axis1 = self.file.createIfcDirection(V(0, 1))
|
transform.Axis1 = self.file.createIfcDirection(ifc_safe_vector_type(V(0, 1)))
|
||||||
transform.Axis2 = self.file.createIfcDirection(V(1, 0))
|
transform.Axis2 = self.file.createIfcDirection(ifc_safe_vector_type(V(1, 0)))
|
||||||
elif mode == "SLBB":
|
elif mode == "SLBB":
|
||||||
offset = profile.Width/2 + profiles_gap
|
offset = profile.Width/2 + profiles_gap
|
||||||
if mirrored:
|
if mirrored:
|
||||||
transform.Axis1 = self.file.createIfcDirection(V(-1, 0))
|
transform.Axis1 = self.file.createIfcDirection(ifc_safe_vector_type(V(-1, 0)))
|
||||||
transform.LocalOrigin.Coordinates = V(-offset, 0)
|
transform.LocalOrigin.Coordinates = ifc_safe_vector_type(V(-offset, 0))
|
||||||
else:
|
else:
|
||||||
transform.LocalOrigin.Coordinates = V(offset, 0)
|
transform.LocalOrigin.Coordinates = ifc_safe_vector_type(V(offset, 0))
|
||||||
|
|
||||||
return derived_profile
|
return derived_profile
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import bonsai.tool as tool
|
|||||||
from test.bim.bootstrap import NewFile
|
from test.bim.bootstrap import NewFile
|
||||||
from bonsai.tool.loader import Loader as subject
|
from bonsai.tool.loader import Loader as subject
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
import collections
|
import collections
|
||||||
@@ -27,21 +28,42 @@ import ifcopenshell.util.placement
|
|||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from math import cos, sin, pi, tan, radians, degrees, atan, sqrt
|
from math import cos, sin, pi, tan, radians, degrees, atan, sqrt
|
||||||
from typing import Union, Optional, Literal, Any, Sequence
|
from typing import Union, Optional, Literal, Any, Sequence, TYPE_CHECKING
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector
|
||||||
|
|
||||||
V = lambda *x: Vector([float(i) for i in x])
|
|
||||||
PRECISION = 1.0e-5
|
PRECISION = 1.0e-5
|
||||||
|
|
||||||
VectorTuple = type[tuple[float, float, float]]
|
|
||||||
"tuple of 3 `float` values"
|
|
||||||
|
|
||||||
# Support both numpy arrays and python sequences as inputs.
|
if TYPE_CHECKING:
|
||||||
VectorType = Union[Sequence[float], Vector, np.ndarray]
|
# NOTE: mathutils is never used at runtime in ifcopenshell,
|
||||||
|
# only for type checking to ensure methods are compatible with
|
||||||
|
# Blender vectors.
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
|
# Support both numpy arrays and python sequences as inputs.
|
||||||
|
VectorType = Union[Sequence[float], Vector, np.ndarray]
|
||||||
|
else:
|
||||||
|
# Ensure it's exportable, so other modules can reuse it for typing.
|
||||||
|
VectorType = ...
|
||||||
|
|
||||||
SequenceOfVectors = Union[Sequence[VectorType], np.ndarray]
|
SequenceOfVectors = Union[Sequence[VectorType], np.ndarray]
|
||||||
|
|
||||||
|
|
||||||
|
def V(*args: Union[float, VectorType, SequenceOfVectors]) -> npt.NDArray[np.float64]:
|
||||||
|
"""Convert floats / vector / sequence of vectors to numpy array.
|
||||||
|
|
||||||
|
Note that `float` argument type also allows passing ints,
|
||||||
|
which will be converted to floats (a double type) as IfcOpenShell is strict
|
||||||
|
about setting int/float attributes.
|
||||||
|
"""
|
||||||
|
if isinstance(args[0], (float, int)):
|
||||||
|
return np.array(args, dtype="d")
|
||||||
|
|
||||||
|
assert len(args) == 1, "Only single argument is supported if providing a vector or a sequence of them."
|
||||||
|
return np.array(args[0], dtype="d")
|
||||||
|
|
||||||
|
|
||||||
def ifc_safe_vector_type(v: Union[VectorType, SequenceOfVectors]) -> Any:
|
def ifc_safe_vector_type(v: Union[VectorType, SequenceOfVectors]) -> Any:
|
||||||
"""Convert vector / sequence of vectors to a list of floats
|
"""Convert vector / sequence of vectors to a list of floats
|
||||||
that's safe to save IFC attribute.
|
that's safe to save IFC attribute.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import ifcopenshell.api.unit
|
|||||||
import ifcopenshell.geom
|
import ifcopenshell.geom
|
||||||
import ifcopenshell.ifcopenshell_wrapper as W
|
import ifcopenshell.ifcopenshell_wrapper as W
|
||||||
import ifcopenshell.util.shape
|
import ifcopenshell.util.shape
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||||
from typing import get_args
|
from typing import get_args
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user