Fix #2906. Basic support added for perspective drawings.

This commit is contained in:
Dion Moult
2023-07-30 16:50:17 +10:00
parent 2015f0618a
commit 794212c223
3 changed files with 105 additions and 27 deletions
@@ -202,7 +202,7 @@ class CreateDrawing(bpy.types.Operator):
@classmethod
def poll(cls, context):
return bool(tool.Ifc.get() and tool.Drawing.is_camera_orthographic() and tool.Drawing.is_drawing_active())
return bool(tool.Ifc.get() and tool.Drawing.is_drawing_active())
def invoke(self, context, event):
# printing all drawings on shift+click
@@ -251,14 +251,20 @@ class CreateDrawing(bpy.types.Operator):
self.svg_writer.setup_drawing_resource_paths(self.camera_element)
underlay_svg = None
linework_svg = None
annotation_svg = None
with profile("Generate underlay"):
underlay_svg = self.generate_underlay(context)
with profile("Generate linework"):
linework_svg = self.generate_linework(context)
if tool.Drawing.is_camera_orthographic():
linework_svg = self.generate_linework(context)
with profile("Generate annotation"):
annotation_svg = self.generate_annotation(context)
if tool.Drawing.is_camera_orthographic():
annotation_svg = self.generate_annotation(context)
with profile("Combine SVG layers"):
svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg)
+36 -10
View File
@@ -589,6 +589,12 @@ class Drawing(blenderbim.core.tool.Drawing):
def import_drawing(cls, drawing):
settings = ifcopenshell.geom.settings()
settings.set(settings.STRICT_TOLERANCE, True)
camera_type = "ORTHO"
body = ifcopenshell.util.representation.get_representation(drawing, "Model", "Body", "MODEL_VIEW")
if "IfcRectangularPyramid" in {e.is_a() for e in tool.Ifc.get().traverse(body)}:
camera_type = "PERSP"
shape = ifcopenshell.geom.create_shape(settings, drawing)
geometry = shape.geometry
@@ -601,17 +607,37 @@ class Drawing(blenderbim.core.tool.Drawing):
depth = max(z) - min(z)
camera = bpy.data.cameras.new(tool.Loader.get_mesh_name(geometry))
camera.type = "ORTHO"
camera.ortho_scale = width if width > height else height
camera.clip_start = 0.002
camera.clip_end = depth
camera.type = camera_type
camera.show_limits = True
if width > height:
camera.BIMCameraProperties.raster_x = 1000
camera.BIMCameraProperties.raster_y = round(1000 * (height / width))
else:
camera.BIMCameraProperties.raster_x = round(1000 * (width / height))
camera.BIMCameraProperties.raster_y = 1000
if camera_type == "ORTHO":
camera.ortho_scale = width if width > height else height
camera.clip_start = 0.002 # Technically 0, but Blender doesn't allow this, so 2mm it is!
camera.clip_end = depth
if width > height:
camera.BIMCameraProperties.raster_x = 1000
camera.BIMCameraProperties.raster_y = round(1000 * (height / width))
else:
camera.BIMCameraProperties.raster_x = round(1000 * (width / height))
camera.BIMCameraProperties.raster_y = 1000
elif camera_type == "PERSP":
abs_min_z = abs(min(z))
abs_max_z = abs(max(z))
camera.clip_start = abs_max_z
camera.clip_end = abs_min_z
max_res = 1000
if width > height:
fov = 2 * math.atan(width / (2 * abs_min_z))
camera.BIMCameraProperties.raster_x = max_res
camera.BIMCameraProperties.raster_y = int(max_res / (width / height))
else:
fov = 2 * math.atan(height / (2 * abs_min_z))
camera.BIMCameraProperties.raster_y = max_res
camera.BIMCameraProperties.raster_x = int(max_res * (width / height))
camera.angle = fov
psets = ifcopenshell.util.element.get_psets(drawing)
pset = psets.get("EPset_Drawing")
@@ -17,6 +17,7 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import bpy
import math
import bmesh
import ifcopenshell.util.unit
from mathutils import Vector, Matrix
@@ -239,7 +240,10 @@ class Usecase:
elif isinstance(self.settings["geometry"], bpy.types.Curve):
return self.create_curve3d_representation()
elif isinstance(self.settings["geometry"], bpy.types.Camera):
return self.create_camera_block_representation()
if self.settings["geometry"].type == "ORTHO":
return self.create_camera_block_representation()
elif self.settings["geometry"].type == "PERSP":
return self.create_camera_pyramid_representation()
elif not len(self.settings["geometry"].edges):
return self.create_point_cloud_representation()
elif not len(self.settings["geometry"].polygons):
@@ -269,14 +273,12 @@ class Usecase:
block = self.file.create_entity(
"IfcBlock",
**{
"Position": self.file.createIfcAxis2Placement3D(
self.create_cartesian_point(-width / 2, -height / 2, -self.settings["geometry"].clip_end)
),
"XLength": self.convert_si_to_unit(width),
"YLength": self.convert_si_to_unit(height),
"ZLength": self.convert_si_to_unit(self.settings["geometry"].clip_end),
},
Position=self.file.createIfcAxis2Placement3D(
self.create_cartesian_point(-width / 2, -height / 2, -self.settings["geometry"].clip_end)
),
XLength=self.convert_si_to_unit(width),
YLength=self.convert_si_to_unit(height),
ZLength=self.convert_si_to_unit(self.settings["geometry"].clip_end),
)
return self.file.createIfcShapeRepresentation(
@@ -286,6 +288,48 @@ class Usecase:
[self.file.createIfcCsgSolid(block)],
)
def create_camera_pyramid_representation(self):
raster_x = self.settings["geometry"].BIMCameraProperties.raster_x
raster_y = self.settings["geometry"].BIMCameraProperties.raster_y
fov = self.settings["geometry"].angle
clip_end = self.settings["geometry"].clip_end
clip_start = self.settings["geometry"].clip_start
if self.is_camera_landscape():
half_width = math.tan(fov / 2) * clip_end
half_height = half_width * raster_y / raster_x
else:
half_height = math.tan(fov / 2) * clip_end
half_width = half_height * raster_x / raster_y
x_length = 2 * half_width
y_length = 2 * half_height
pyramid = self.file.create_entity(
"IfcRectangularPyramid",
Position=self.file.createIfcAxis2Placement3D(
self.create_cartesian_point(-x_length / 2, -y_length / 2, -clip_end)
),
XLength=self.convert_si_to_unit(x_length),
YLength=self.convert_si_to_unit(y_length),
Height=self.convert_si_to_unit(clip_end),
)
surface = self.file.createIfcPlane(
self.file.createIfcAxis2Placement3D(self.create_cartesian_point(0, 0, -clip_start))
)
half_space = self.file.createIfcHalfSpaceSolid(surface, False)
clipping_result = self.file.create_entity("IfcBooleanClippingResult", "DIFFERENCE", pyramid, half_space)
return self.file.createIfcShapeRepresentation(
self.settings["context"],
self.settings["context"].ContextIdentifier,
"CSG",
[self.file.createIfcCsgSolid(clipping_result)],
)
def is_camera_landscape(self):
return (
self.settings["geometry"].BIMCameraProperties.raster_x
@@ -372,6 +416,7 @@ class Usecase:
def is_mesh_curve_consequtive(self, geom_data):
import blenderbim.tool as tool
bm = tool.Blender.get_bmesh_for_mesh(geom_data)
bm.verts.ensure_lookup_table()
start_vert = bm.verts[0]
@@ -387,6 +432,7 @@ class Usecase:
processed_verts = set()
processed_verts.add(start_vert)
def validate_edge(edge, start_vert, processed_verts):
cur_vert = edge.other_vert(start_vert)
while True:
@@ -403,13 +449,13 @@ class Usecase:
edge = next(e for e in edges if e != edge)
cur_vert = edge.other_vert(cur_vert)
return True
if not validate_edge(edge0, start_vert, processed_verts):
return
if edge1 and not validate_edge(edge1, start_vert, processed_verts):
return
if len(processed_verts) != n_verts:
return False
return True
@@ -479,6 +525,7 @@ class Usecase:
def remove_doubles_from_mesh(self, mesh):
import blenderbim.tool as tool
bm = tool.Blender.get_bmesh_for_mesh(mesh)
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
tool.Blender.apply_bmesh(mesh, bm)
@@ -488,8 +535,7 @@ class Usecase:
self.remove_doubles_from_mesh(geom_data)
curves = []
points = [
self.create_cartesian_point(v.co.x, v.co.y, v.co.z if not is_2d else None)
for v in geom_data.vertices
self.create_cartesian_point(v.co.x, v.co.y, v.co.z if not is_2d else None) for v in geom_data.vertices
]
coord_list = [p.Coordinates for p in points]
edge_loops = []