mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
Fix #2906. Basic support added for perspective drawings.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
Reference in New Issue
Block a user