Add basic support for sloped walls, sloping along the local X axis

This commit is contained in:
Dion Moult
2022-10-17 22:52:28 +11:00
parent f8a8250429
commit 9ef65c9cc5
5 changed files with 66 additions and 8 deletions
@@ -31,6 +31,7 @@ classes = (
workspace.Hotkey, workspace.Hotkey,
wall.AlignWall, wall.AlignWall,
wall.ChangeExtrusionDepth, wall.ChangeExtrusionDepth,
wall.ChangeExtrusionXAngle,
wall.ChangeLayerLength, wall.ChangeLayerLength,
wall.FlipWall, wall.FlipWall,
wall.JoinWall, wall.JoinWall,
@@ -229,6 +229,7 @@ class BIMModelProperties(PropertyGroup):
y: bpy.props.FloatProperty(name="Y", default=0.5) y: bpy.props.FloatProperty(name="Y", default=0.5)
z: bpy.props.FloatProperty(name="Z", default=0.5) z: bpy.props.FloatProperty(name="Z", default=0.5)
rl: bpy.props.FloatProperty(name="RL", default=1) rl: bpy.props.FloatProperty(name="RL", default=1)
x_angle: bpy.props.FloatProperty(name="X Angle", default=0)
type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page) type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page)
type_template: bpy.props.EnumProperty( type_template: bpy.props.EnumProperty(
items=( items=(
@@ -34,7 +34,7 @@ import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.pset.data import Data as PsetData from ifcopenshell.api.pset.data import Data as PsetData
from ifcopenshell.api.material.data import Data as MaterialData from ifcopenshell.api.material.data import Data as MaterialData
from math import pi, degrees from math import pi, sin, cos, degrees, radians
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
@@ -243,6 +243,37 @@ class ChangeExtrusionDepth(bpy.types.Operator, tool.Ifc.Operator):
return {"FINISHED"} return {"FINISHED"}
class ChangeExtrusionXAngle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.change_extrusion_x_angle"
bl_label = "Change Extrusion X Angle"
bl_options = {"REGISTER", "UNDO"}
x_angle: bpy.props.FloatProperty()
@classmethod
def poll(cls, context):
return context.selected_objects
def _execute(self, context):
wall_objs = []
x_angle = radians(self.x_angle)
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
return
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if not representation:
return
extrusion = tool.Model.get_extrusion(representation)
if not extrusion:
return
extrusion.ExtrudedDirection.DirectionRatios = (0.0, sin(x_angle), cos(x_angle))
if element.is_a("IfcWall"):
wall_objs.append(obj)
if wall_objs:
DumbWallRecalculator().recalculate(wall_objs)
return {"FINISHED"}
class ChangeLayerLength(bpy.types.Operator, tool.Ifc.Operator): class ChangeLayerLength(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.change_layer_length" bl_idname = "bim.change_layer_length"
bl_label = "Change Layer Length" bl_label = "Change Layer Length"
@@ -419,6 +450,7 @@ class DumbWallGenerator:
self.length = props.length * self.unit_scale self.length = props.length * self.unit_scale
self.rotation = 0.0 self.rotation = 0.0
self.location = Vector((0, 0, 0)) self.location = Vector((0, 0, 0))
self.x_angle = 0 if tool.Cad.is_x(props.x_angle, 0, tolerance=0.001) else radians(props.x_angle)
if self.has_sketch(): if self.has_sketch():
return # For now return # For now
@@ -571,6 +603,7 @@ class DumbWallGenerator:
offset=self.layers["offset"], offset=self.layers["offset"],
length=self.length, length=self.length,
height=self.height, height=self.height,
x_angle=self.x_angle,
) )
ifcopenshell.api.run( ifcopenshell.api.run(
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=representation "geometry.assign_representation", tool.Ifc.get(), product=element, representation=representation
@@ -993,7 +1026,9 @@ class DumbWallJoiner:
axis = body = tool.Model.get_wall_axis(obj)["reference"] axis = body = tool.Model.get_wall_axis(obj)["reference"]
self.axis = copy.deepcopy(axis) self.axis = copy.deepcopy(axis)
self.body = copy.deepcopy(body) self.body = copy.deepcopy(body)
height = self.get_height(tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)) extrusion_data = self.get_extrusion_data(tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id))
height = extrusion_data["height"]
x_angle = extrusion_data["x_angle"]
self.clippings = [] self.clippings = []
layers = tool.Model.get_material_layer_parameters(element) layers = tool.Model.get_material_layer_parameters(element)
@@ -1045,6 +1080,7 @@ class DumbWallJoiner:
context=self.body_context, context=self.body_context,
length=length, length=length,
height=height, height=height,
x_angle=x_angle,
offset=layers["offset"], offset=layers["offset"],
thickness=layers["thickness"], thickness=layers["thickness"],
clippings=self.clippings, clippings=self.clippings,
@@ -1106,18 +1142,21 @@ class DumbWallJoiner:
) )
) )
def get_height(self, representation): def get_extrusion_data(self, representation):
height = 3.0 results = {"height": 3.0, "x_angle": 0}
item = representation.Items[0] item = representation.Items[0]
while True: while True:
if item.is_a("IfcExtrudedAreaSolid"): if item.is_a("IfcExtrudedAreaSolid"):
height = item.Depth * self.unit_scale results["height"] = item.Depth * self.unit_scale
x, y, z = item.ExtrudedDirection.DirectionRatios
if not tool.Cad.is_x(x, 0) or not tool.Cad.is_x(y, 0) or not tool.Cad.is_x(z, 1):
results["x_angle"] = Vector((0, 1)).angle(Vector((y, z)))
break break
elif item.is_a("IfcBooleanClippingResult"): elif item.is_a("IfcBooleanClippingResult"):
item = item.FirstOperand item = item.FirstOperand
else: else:
break break
return height return results
def join(self, wall1, wall2, connection1, connection2, is_relating=True, description="BUTT"): def join(self, wall1, wall2, connection1, connection2, is_relating=True, description="BUTT"):
element1 = tool.Ifc.get_entity(wall1) element1 = tool.Ifc.get_entity(wall1)
@@ -101,6 +101,9 @@ class BimToolUI:
row = cls.layout.row(align=True) row = cls.layout.row(align=True)
row.prop(data=cls.props, property="length", text="Length") row.prop(data=cls.props, property="length", text="Length")
row = cls.layout.row(align=True)
row.prop(data=cls.props, property="x_angle", text="X Angle")
elif cls.props.ifc_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"): elif cls.props.ifc_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"):
row = cls.layout.row(align=True) row = cls.layout.row(align=True)
row.prop(data=cls.props, property="cardinal_point", text="Axis") row.prop(data=cls.props, property="cardinal_point", text="Axis")
@@ -125,6 +128,11 @@ class BimToolUI:
op = row.operator("bim.change_layer_length", icon="FILE_REFRESH", text="") op = row.operator("bim.change_layer_length", icon="FILE_REFRESH", text="")
op.length = cls.props.length op.length = cls.props.length
row = cls.layout.row(align=True)
row.prop(data=cls.props, property="x_angle", text="X Angle")
op = row.operator("bim.change_extrusion_x_angle", icon="FILE_REFRESH", text="")
op.x_angle = cls.props.x_angle
row = cls.layout.row(align=True) row = cls.layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT") row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_E") row.label(text="", icon="EVENT_E")
@@ -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 math import sin, cos
import ifcopenshell.util.unit import ifcopenshell.util.unit
@@ -28,10 +29,12 @@ class Usecase:
"height": 3.0, "height": 3.0,
"offset": 0.0, "offset": 0.0,
"thickness": 0.2, "thickness": 0.2,
# Sloped walls along the wall's X axis, provided in radians
"x_angle": 0,
# Planes are defined as a matrix. The XY plane is the clipping boundary and +Z is removed. # Planes are defined as a matrix. The XY plane is the clipping boundary and +Z is removed.
# [{"type": "IfcBooleanClippingResult", "operand_type": "IfcHalfSpaceSolid", "matrix": [...]}, {...}] # [{"type": "IfcBooleanClippingResult", "operand_type": "IfcHalfSpaceSolid", "matrix": [...]}, {...}]
"clippings": [], # A list of planes that define clipping half space solids "clippings": [], # A list of planes that define clipping half space solids
"booleans": [], # Any existing IfcBooleanResults "booleans": [], # Any existing IfcBooleanResults
} }
for key, value in settings.items(): for key, value in settings.items():
self.settings[key] = value self.settings[key] = value
@@ -59,6 +62,12 @@ class Usecase:
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points]) curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
else: else:
curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points), None, False) curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points), None, False)
if self.settings["x_angle"]:
extrusion_direction = self.file.createIfcDirection(
(0.0, sin(self.settings["x_angle"]), cos(self.settings["x_angle"]))
)
else:
extrusion_direction = self.file.createIfcDirection((0.0, 0.0, 1.0))
extrusion = self.file.createIfcExtrudedAreaSolid( extrusion = self.file.createIfcExtrudedAreaSolid(
self.file.createIfcArbitraryClosedProfileDef("AREA", None, curve), self.file.createIfcArbitraryClosedProfileDef("AREA", None, curve),
self.file.createIfcAxis2Placement3D( self.file.createIfcAxis2Placement3D(
@@ -66,7 +75,7 @@ class Usecase:
self.file.createIfcDirection((0.0, 0.0, 1.0)), self.file.createIfcDirection((0.0, 0.0, 1.0)),
self.file.createIfcDirection((1.0, 0.0, 0.0)), self.file.createIfcDirection((1.0, 0.0, 0.0)),
), ),
self.file.createIfcDirection((0.0, 0.0, 1.0)), extrusion_direction,
self.convert_si_to_unit(self.settings["height"]), self.convert_si_to_unit(self.settings["height"]),
) )
if self.settings["booleans"]: if self.settings["booleans"]: