mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
#1153. Section references are now auto generated when activating a drawing.
This commit is contained in:
@@ -40,7 +40,7 @@ class BaseDecorator:
|
||||
|
||||
DEF_GLSL = """
|
||||
#define PI 3.141592653589793
|
||||
#define MAX_POINTS 32
|
||||
#define MAX_POINTS 64
|
||||
#define CIRCLE_SEGS 12
|
||||
"""
|
||||
|
||||
@@ -1162,15 +1162,13 @@ class ElevationDecorator(LevelDecorator):
|
||||
|
||||
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
||||
vec4 edge = p1w - p0w, dir = normalize(edge);
|
||||
// vec4 dir = normalize(vec4(1, 0, 0, 0));
|
||||
vec4 gap = dir * viewportDrawingScale * TRIANGLE_L * .5;
|
||||
vec4 side = vec4(cross(vec3(dir.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
||||
vec4 p;
|
||||
|
||||
vec4 head[CIRCLE_SEGS];
|
||||
circle_head(viewportDrawingScale * CIRCLE_SIZE, head);
|
||||
|
||||
vec4 head3[5];
|
||||
vec4 head5[5];
|
||||
|
||||
// start edge circle
|
||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||
@@ -1183,21 +1181,21 @@ class ElevationDecorator(LevelDecorator):
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// start edge triangle
|
||||
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head3);
|
||||
p = p0w + head3[0];
|
||||
// edge triangle
|
||||
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||
p = p0w + head5[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head3[1];
|
||||
p = p0w + head5[1];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head3[2];
|
||||
p = p0w + head5[2];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head3[3];
|
||||
p = p0w + head5[3];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head3[4];
|
||||
p = p0w + head5[4];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
@@ -1220,15 +1218,15 @@ class ElevationDecorator(LevelDecorator):
|
||||
self.draw_lines(context, obj, [v1, v2], [(0, 1)])
|
||||
|
||||
|
||||
class SectionViewDecorator(LevelDecorator):
|
||||
class SectionDecorator(LevelDecorator):
|
||||
objecttype = "SECTION"
|
||||
|
||||
DEF_GLSL = (
|
||||
BaseDecorator.DEF_GLSL
|
||||
+ """
|
||||
#define CIRCLE_SIZE 8.0
|
||||
#define TRIANGLE_L 32.0
|
||||
#define TRIANGLE_W 16.0
|
||||
#define TRIANGLE_L 22.63
|
||||
#define TRIANGLE_W 11.31
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -1239,12 +1237,14 @@ class SectionViewDecorator(LevelDecorator):
|
||||
layout(lines) in;
|
||||
layout(line_strip, max_vertices=MAX_POINTS) out;
|
||||
|
||||
void triangle_head(in vec4 dir, in vec4 side, in float length, in float width, out vec4 head[3]) {
|
||||
vec4 nose = dir * length;
|
||||
vec4 ear = side * width;
|
||||
head[0] = vec4(0);
|
||||
head[1] = nose * .5 + ear;
|
||||
head[2] = nose;
|
||||
void triangle_head(in vec4 side, in vec4 dir, in float length, in float width, in float radius, out vec4 head[5]) {
|
||||
vec4 nose = side * length;
|
||||
vec4 ear = dir * width;
|
||||
head[0] = side * -radius;
|
||||
head[1] = nose * -.5;
|
||||
head[2] = vec4(0) + ear;
|
||||
head[3] = nose * .5;
|
||||
head[4] = side * radius;
|
||||
}
|
||||
|
||||
void main() {
|
||||
@@ -1254,76 +1254,114 @@ class SectionViewDecorator(LevelDecorator):
|
||||
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||
|
||||
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
||||
vec4 edge = p1w - p0w, dir = normalize(edge);
|
||||
vec4 gap = dir * viewportDrawingScale * TRIANGLE_L * .5;
|
||||
vec4 side = vec4(cross(vec3(dir.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
||||
vec4 edge = p1w - p0w, side = normalize(edge);
|
||||
vec4 gap = side * viewportDrawingScale * CIRCLE_SIZE;
|
||||
vec4 dir = vec4(cross(vec3(side.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
||||
vec4 p;
|
||||
|
||||
vec4 head[CIRCLE_SEGS];
|
||||
circle_head(viewportDrawingScale * CIRCLE_SIZE, head);
|
||||
|
||||
vec4 head3[3];
|
||||
vec4 head5[5];
|
||||
|
||||
// start edge circle
|
||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||
p = p0w + gap + head[i];
|
||||
p = p0w + head[i];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
}
|
||||
p = p0w + gap + head[0];
|
||||
p = p0w + head[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// start edge triangle
|
||||
triangle_head(dir, -side, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, head3);
|
||||
p = p0w + head3[0];
|
||||
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||
p = p0w + head5[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head3[1];
|
||||
p = p0w + head5[1];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head3[2];
|
||||
p = p0w + head5[2];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head5[3];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + head5[4];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// start reference divider
|
||||
p = p0w + normalize(vec4(-1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p0w + normalize(vec4(1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// end edge circle
|
||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||
p = p1w - gap + head[i];
|
||||
p = p1w - head[i];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
}
|
||||
p = p1w - gap + head[0];
|
||||
p = p1w - head[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// end edge triangle
|
||||
triangle_head(-dir, -side, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, head3);
|
||||
p = p1w + head3[0];
|
||||
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||
p = p1w + head5[0];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head3[1];
|
||||
p = p1w + head5[1];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head3[2];
|
||||
p = p1w + head5[2];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head5[3];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + head5[4];
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// end reference divider
|
||||
p = p1w + normalize(vec4(-1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
p = p1w + normalize(vec4(1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
|
||||
// stem
|
||||
gl_Position = p0;
|
||||
p = p0w + gap;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
gl_Position = p1;
|
||||
p = p1w - gap;
|
||||
gl_Position = WIN2CLIP(p);
|
||||
EmitVertex();
|
||||
EndPrimitive();
|
||||
}
|
||||
"""
|
||||
|
||||
def decorate(self, context, obj):
|
||||
verts, _, _ = self.get_path_geom(obj, topo=False)
|
||||
self.draw_lines(context, obj, verts, [(0, 1)])
|
||||
if obj.data.is_editmode:
|
||||
verts, idxs = self.get_editmesh_geom(obj)
|
||||
else:
|
||||
verts, idxs = self.get_mesh_geom(obj)
|
||||
vert_map = {v.freeze(): (obj.matrix_world.inverted() @ v).x for v in verts}
|
||||
verts = sorted(verts, key=lambda v: vert_map[v], reverse=True)
|
||||
self.draw_lines(context, obj, verts, idxs)
|
||||
|
||||
|
||||
|
||||
class TextDecorator(BaseDecorator):
|
||||
@@ -1422,7 +1460,7 @@ class DecorationsHandler:
|
||||
SectionLevelDecorator,
|
||||
StairDecorator,
|
||||
BreakDecorator,
|
||||
SectionViewDecorator,
|
||||
SectionDecorator,
|
||||
ElevationDecorator,
|
||||
TextDecorator,
|
||||
]
|
||||
|
||||
@@ -260,14 +260,11 @@ def format_distance(value, isArea=False, hide_units=True):
|
||||
def get_active_drawing(scene):
|
||||
"""Get active drawing collection and camera"""
|
||||
props = scene.DocProperties
|
||||
if props.active_drawing_index is None or len(props.drawings) == 0:
|
||||
return None, None
|
||||
try:
|
||||
drawing = props.active_drawing
|
||||
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(drawing.ifc_definition_id))
|
||||
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id))
|
||||
return camera.users_collection[0], camera
|
||||
except (KeyError, IndexError):
|
||||
raise RuntimeError("missing drawing collection")
|
||||
except:
|
||||
return None, None
|
||||
|
||||
|
||||
def get_project_collection(scene):
|
||||
|
||||
@@ -82,6 +82,11 @@ class AddDrawing(bpy.types.Operator, Operator):
|
||||
target_view=self.props.target_view,
|
||||
location_hint=hint,
|
||||
)
|
||||
try:
|
||||
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class CreateDrawing(bpy.types.Operator):
|
||||
@@ -777,12 +782,10 @@ class RemoveDrawing(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_drawing"
|
||||
bl_label = "Remove Drawing"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
index: bpy.props.IntProperty()
|
||||
drawing: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
drawing = tool.Ifc.get().by_id(props.drawings[self.index].ifc_definition_id)
|
||||
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=drawing)
|
||||
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
|
||||
|
||||
class AddDrawingStyle(bpy.types.Operator):
|
||||
|
||||
@@ -293,6 +293,7 @@ class DocProperties(PropertyGroup):
|
||||
)
|
||||
location_hint: EnumProperty(items=get_location_hint, name="Location Hint")
|
||||
drawings: CollectionProperty(name="Drawings", type=Drawing)
|
||||
active_drawing_id: IntProperty(name="Active Drawing Id")
|
||||
active_drawing_index: IntProperty(name="Active Drawing Index")
|
||||
current_drawing_index: IntProperty(name="Current Drawing Index")
|
||||
schedules: CollectionProperty(name="Schedules", type=Schedule)
|
||||
@@ -312,10 +313,6 @@ class DocProperties(PropertyGroup):
|
||||
def active_schedule(self):
|
||||
return self.schedules[self.active_schedule_index]
|
||||
|
||||
@property
|
||||
def active_drawing(self):
|
||||
return self.drawings[self.active_drawing_index]
|
||||
|
||||
@property
|
||||
def active_sheet(self):
|
||||
return self.sheets[self.active_sheet_index]
|
||||
|
||||
@@ -172,14 +172,15 @@ class BIM_PT_drawings(Panel):
|
||||
|
||||
if self.props.drawings:
|
||||
if self.props.active_drawing_index < len(self.props.drawings):
|
||||
active_drawing = self.props.drawings[self.props.active_drawing_index]
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
op = row.operator("bim.open_view", icon="URL", text="")
|
||||
op.view = self.props.active_drawing.name
|
||||
op.view = active_drawing.name
|
||||
op = row.operator("bim.activate_view", icon="OUTLINER_OB_CAMERA", text="")
|
||||
op.drawing = self.props.active_drawing.ifc_definition_id
|
||||
op.drawing = active_drawing.ifc_definition_id
|
||||
row.operator("bim.create_drawing", text="", icon="OUTPUT")
|
||||
row.operator("bim.remove_drawing", icon="X", text="").index = self.props.active_drawing_index
|
||||
row.operator("bim.remove_drawing", icon="X", text="").drawing = active_drawing.ifc_definition_id
|
||||
self.layout.template_list(
|
||||
"BIM_UL_drawinglist", "", self.props, "drawings", self.props, "active_drawing_index"
|
||||
)
|
||||
|
||||
@@ -413,60 +413,15 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
target_view = psets.get("EPset_Drawing", {}).get("TargetView", None)
|
||||
if target_view == "ELEVATION_VIEW":
|
||||
return cls.generate_elevation_reference_annotation(drawing, reference_element, context)
|
||||
elif target_view == "SECTION_VIEW":
|
||||
return cls.generate_section_reference_annotation(drawing, reference_element, context)
|
||||
|
||||
@classmethod
|
||||
def generate_elevation_reference_annotation(cls, drawing, reference_element, context):
|
||||
def generate_section_reference_annotation(cls, drawing, reference_element, context):
|
||||
reference_obj = tool.Ifc.get_object(reference_element)
|
||||
reference_obj.matrix_world
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
|
||||
def is_perpendicular(a, b):
|
||||
axes = [mathutils.Vector((1, 0, 0)), mathutils.Vector((0, 1, 0)), mathutils.Vector((0, 0, 1))]
|
||||
a_quaternion = a.matrix_world.to_quaternion()
|
||||
b_quaternion = b.matrix_world.to_quaternion()
|
||||
for axis in axes:
|
||||
if abs((a_quaternion @ axis).angle((b_quaternion @ axis)) - (math.pi / 2)) < 1e-5:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_camera_block(obj):
|
||||
raster_x = obj.data.BIMCameraProperties.raster_x
|
||||
raster_y = obj.data.BIMCameraProperties.raster_y
|
||||
|
||||
if raster_x > raster_y:
|
||||
width = obj.data.ortho_scale
|
||||
height = width / raster_x * raster_y
|
||||
else:
|
||||
height = obj.data.ortho_scale
|
||||
width = height / raster_y * raster_x
|
||||
depth = obj.data.clip_end
|
||||
|
||||
verts = (
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, -height /2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, -height /2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, height /2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, height /2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, -height /2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, -height /2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, height /2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, height /2, 0))
|
||||
)
|
||||
faces = [
|
||||
[0, 1, 3, 2],
|
||||
[2, 3, 7, 6],
|
||||
[6, 7, 5, 4],
|
||||
[4, 5, 1, 0],
|
||||
[2, 6, 4, 0],
|
||||
[7, 3, 1, 5],
|
||||
]
|
||||
return {"verts": verts, "faces": faces}
|
||||
|
||||
def is_intersecting(a, b):
|
||||
a_block = get_camera_block(a)
|
||||
a_tree = mathutils.bvhtree.BVHTree.FromPolygons(a_block["verts"], a_block["faces"])
|
||||
b_block = get_camera_block(b)
|
||||
b_tree = mathutils.bvhtree.BVHTree.FromPolygons(b_block["verts"], b_block["faces"])
|
||||
return bool(a_tree.overlap(b_tree))
|
||||
bounds = helper.ortho_view_frame(camera.data) if camera.data.type == "ORTHO" else None
|
||||
|
||||
def to_camera_coords(camera, reference_obj):
|
||||
mat = reference_obj.matrix_world.copy()
|
||||
@@ -476,14 +431,90 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
mat[0][3] = xyz[0]
|
||||
mat[1][3] = xyz[1]
|
||||
mat[2][3] = xyz[2]
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start))
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start - 0.05))
|
||||
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||
mat[0][3] += annotation_offset[0]
|
||||
mat[1][3] += annotation_offset[1]
|
||||
mat[2][3] += annotation_offset[2]
|
||||
return mat
|
||||
|
||||
if is_perpendicular(camera, reference_obj) and is_intersecting(camera, reference_obj):
|
||||
def clip_to_camera_boundary(mesh, bounds):
|
||||
mesh.verts.ensure_lookup_table()
|
||||
points = [v.co for v in mesh.verts[0:2]]
|
||||
points = helper.clip_segment(bounds, points)
|
||||
if points is None:
|
||||
return None
|
||||
mesh.verts[0].co = points[0]
|
||||
mesh.verts[1].co = points[1]
|
||||
return mesh
|
||||
|
||||
if cls.is_perpendicular(camera, reference_obj) and cls.is_intersecting(camera, reference_obj):
|
||||
reference_mesh = cls.get_camera_block(reference_obj)
|
||||
obj_matrix = to_camera_coords(camera, reference_obj)
|
||||
|
||||
# The reference mesh vertices represent a view cube. To convert this into a section line we:
|
||||
# 1. Select the 4 +Z vertices local to the reference element. This is the cutting plane.
|
||||
verts_local_to_reference = [reference_obj.matrix_world.inverted() @ v for v in reference_mesh["verts"]]
|
||||
cutting_plane_verts = sorted(verts_local_to_reference, key=lambda x: x.z)[-4:]
|
||||
# Filter verts with the same XY coords
|
||||
#set([v.xy for v in cutting_plane_verts])
|
||||
|
||||
global_cutting_plane_verts = [reference_obj.matrix_world @ v for v in cutting_plane_verts]
|
||||
# 2. Project the cutting plane onto our viewing camera.
|
||||
verts_local_to_camera = [camera.matrix_world.inverted() @ v for v in global_cutting_plane_verts]
|
||||
# 3. Collapse verts with the same XY coords, and set Z to be just below the clip_start so it's visible
|
||||
collapsed_verts = []
|
||||
for vert in verts_local_to_camera:
|
||||
if not [True for v in collapsed_verts if (vert.xy - v.xy).length < 1e-2]:
|
||||
collapsed_verts.append(mathutils.Vector((vert.x, vert.y, -camera.data.clip_start - 0.05)))
|
||||
# 4. The first two vertices is the section line
|
||||
section_line = collapsed_verts[0:2]
|
||||
global_section_line = [camera.matrix_world @ v for v in section_line]
|
||||
local_section_line = [obj_matrix.inverted() @ v for v in global_section_line]
|
||||
|
||||
mesh = bpy.data.meshes.new(name="Annotation")
|
||||
mesh.from_pydata(local_section_line, [(0, 1)], [])
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
bm = clip_to_camera_boundary(bm, bounds)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
|
||||
obj = bpy.data.objects.new(reference_obj.name, mesh)
|
||||
obj.matrix_world = obj_matrix
|
||||
|
||||
element = cls.run_root_assign_class(
|
||||
obj=obj,
|
||||
ifc_class="IfcAnnotation",
|
||||
predefined_type="SECTION",
|
||||
should_add_representation=True,
|
||||
context=context,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def generate_elevation_reference_annotation(cls, drawing, reference_element, context):
|
||||
reference_obj = tool.Ifc.get_object(reference_element)
|
||||
reference_obj.matrix_world
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
|
||||
def to_camera_coords(camera, reference_obj):
|
||||
mat = reference_obj.matrix_world.copy()
|
||||
xyz = camera.matrix_world.inverted() @ reference_obj.matrix_world.translation
|
||||
xyz[2] = 0
|
||||
xyz = camera.matrix_world @ xyz
|
||||
mat[0][3] = xyz[0]
|
||||
mat[1][3] = xyz[1]
|
||||
mat[2][3] = xyz[2]
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start - 0.05))
|
||||
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||
mat[0][3] += annotation_offset[0]
|
||||
mat[1][3] += annotation_offset[1]
|
||||
mat[2][3] += annotation_offset[2]
|
||||
return mat
|
||||
|
||||
if cls.is_perpendicular(camera, reference_obj) and cls.is_intersecting(camera, reference_obj):
|
||||
obj = bpy.data.objects.new(reference_obj.name, None)
|
||||
obj.empty_display_size = 0.1
|
||||
obj.matrix_world = to_camera_coords(camera, reference_obj)
|
||||
@@ -530,7 +561,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
def to_camera_coords(obj, mesh):
|
||||
mesh.transform(camera.matrix_world.inverted() @ obj.matrix_world)
|
||||
obj.matrix_world = camera.matrix_world
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start))
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start - 0.05))
|
||||
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||
obj.matrix_world[0][3] += annotation_offset[0]
|
||||
obj.matrix_world[1][3] += annotation_offset[1]
|
||||
@@ -585,6 +616,57 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def is_perpendicular(cls, a, b):
|
||||
axes = [mathutils.Vector((1, 0, 0)), mathutils.Vector((0, 1, 0)), mathutils.Vector((0, 0, 1))]
|
||||
a_quaternion = a.matrix_world.to_quaternion()
|
||||
b_quaternion = b.matrix_world.to_quaternion()
|
||||
for axis in axes:
|
||||
if abs((a_quaternion @ axis).angle((b_quaternion @ axis)) - (math.pi / 2)) < 1e-5:
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_camera_block(cls, obj):
|
||||
raster_x = obj.data.BIMCameraProperties.raster_x
|
||||
raster_y = obj.data.BIMCameraProperties.raster_y
|
||||
|
||||
if raster_x > raster_y:
|
||||
width = obj.data.ortho_scale
|
||||
height = width / raster_x * raster_y
|
||||
else:
|
||||
height = obj.data.ortho_scale
|
||||
width = height / raster_y * raster_x
|
||||
depth = obj.data.clip_end
|
||||
|
||||
verts = (
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, -height /2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, -height /2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, height /2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((-width / 2, height /2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, -height /2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, -height /2, 0)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, height /2, -depth)),
|
||||
obj.matrix_world @ mathutils.Vector((width / 2, height /2, 0))
|
||||
)
|
||||
faces = [
|
||||
[0, 1, 3, 2],
|
||||
[2, 3, 7, 6],
|
||||
[6, 7, 5, 4],
|
||||
[4, 5, 1, 0],
|
||||
[2, 6, 4, 0],
|
||||
[7, 3, 1, 5],
|
||||
]
|
||||
return {"verts": verts, "faces": faces}
|
||||
|
||||
@classmethod
|
||||
def is_intersecting(cls, a, b):
|
||||
a_block = cls.get_camera_block(a)
|
||||
a_tree = mathutils.bvhtree.BVHTree.FromPolygons(a_block["verts"], a_block["faces"])
|
||||
b_block = cls.get_camera_block(b)
|
||||
b_tree = mathutils.bvhtree.BVHTree.FromPolygons(b_block["verts"], b_block["faces"])
|
||||
return bool(a_tree.overlap(b_tree))
|
||||
|
||||
@classmethod
|
||||
def sync_object_representation(cls, obj):
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
Reference in New Issue
Block a user