mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-28 03:05:33 +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 = """
|
DEF_GLSL = """
|
||||||
#define PI 3.141592653589793
|
#define PI 3.141592653589793
|
||||||
#define MAX_POINTS 32
|
#define MAX_POINTS 64
|
||||||
#define CIRCLE_SEGS 12
|
#define CIRCLE_SEGS 12
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -1162,15 +1162,13 @@ class ElevationDecorator(LevelDecorator):
|
|||||||
|
|
||||||
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
||||||
vec4 edge = p1w - p0w, dir = normalize(edge);
|
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 side = vec4(cross(vec3(dir.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
||||||
vec4 p;
|
vec4 p;
|
||||||
|
|
||||||
vec4 head[CIRCLE_SEGS];
|
vec4 head[CIRCLE_SEGS];
|
||||||
circle_head(viewportDrawingScale * CIRCLE_SIZE, head);
|
circle_head(viewportDrawingScale * CIRCLE_SIZE, head);
|
||||||
|
|
||||||
vec4 head3[5];
|
vec4 head5[5];
|
||||||
|
|
||||||
// start edge circle
|
// start edge circle
|
||||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||||
@@ -1183,21 +1181,21 @@ class ElevationDecorator(LevelDecorator):
|
|||||||
EmitVertex();
|
EmitVertex();
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
// start edge triangle
|
// edge triangle
|
||||||
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head3);
|
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||||
p = p0w + head3[0];
|
p = p0w + head5[0];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
p = p0w + head3[1];
|
p = p0w + head5[1];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
p = p0w + head3[2];
|
p = p0w + head5[2];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
p = p0w + head3[3];
|
p = p0w + head5[3];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
p = p0w + head3[4];
|
p = p0w + head5[4];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
@@ -1220,15 +1218,15 @@ class ElevationDecorator(LevelDecorator):
|
|||||||
self.draw_lines(context, obj, [v1, v2], [(0, 1)])
|
self.draw_lines(context, obj, [v1, v2], [(0, 1)])
|
||||||
|
|
||||||
|
|
||||||
class SectionViewDecorator(LevelDecorator):
|
class SectionDecorator(LevelDecorator):
|
||||||
objecttype = "SECTION"
|
objecttype = "SECTION"
|
||||||
|
|
||||||
DEF_GLSL = (
|
DEF_GLSL = (
|
||||||
BaseDecorator.DEF_GLSL
|
BaseDecorator.DEF_GLSL
|
||||||
+ """
|
+ """
|
||||||
#define CIRCLE_SIZE 8.0
|
#define CIRCLE_SIZE 8.0
|
||||||
#define TRIANGLE_L 32.0
|
#define TRIANGLE_L 22.63
|
||||||
#define TRIANGLE_W 16.0
|
#define TRIANGLE_W 11.31
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1239,12 +1237,14 @@ class SectionViewDecorator(LevelDecorator):
|
|||||||
layout(lines) in;
|
layout(lines) in;
|
||||||
layout(line_strip, max_vertices=MAX_POINTS) out;
|
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]) {
|
void triangle_head(in vec4 side, in vec4 dir, in float length, in float width, in float radius, out vec4 head[5]) {
|
||||||
vec4 nose = dir * length;
|
vec4 nose = side * length;
|
||||||
vec4 ear = side * width;
|
vec4 ear = dir * width;
|
||||||
head[0] = vec4(0);
|
head[0] = side * -radius;
|
||||||
head[1] = nose * .5 + ear;
|
head[1] = nose * -.5;
|
||||||
head[2] = nose;
|
head[2] = vec4(0) + ear;
|
||||||
|
head[3] = nose * .5;
|
||||||
|
head[4] = side * radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -1254,76 +1254,114 @@ class SectionViewDecorator(LevelDecorator):
|
|||||||
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||||
|
|
||||||
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
|
||||||
vec4 edge = p1w - p0w, dir = normalize(edge);
|
vec4 edge = p1w - p0w, side = normalize(edge);
|
||||||
vec4 gap = dir * viewportDrawingScale * TRIANGLE_L * .5;
|
vec4 gap = side * viewportDrawingScale * CIRCLE_SIZE;
|
||||||
vec4 side = vec4(cross(vec3(dir.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
vec4 dir = vec4(cross(vec3(side.xy, 0), vec3(0, 0, 1)).xy, 0, 0);
|
||||||
vec4 p;
|
vec4 p;
|
||||||
|
|
||||||
vec4 head[CIRCLE_SEGS];
|
vec4 head[CIRCLE_SEGS];
|
||||||
circle_head(viewportDrawingScale * CIRCLE_SIZE, head);
|
circle_head(viewportDrawingScale * CIRCLE_SIZE, head);
|
||||||
|
|
||||||
vec4 head3[3];
|
vec4 head5[5];
|
||||||
|
|
||||||
// start edge circle
|
// start edge circle
|
||||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||||
p = p0w + gap + head[i];
|
p = p0w + head[i];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
}
|
}
|
||||||
p = p0w + gap + head[0];
|
p = p0w + head[0];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
// start edge triangle
|
// start edge triangle
|
||||||
triangle_head(dir, -side, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, head3);
|
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||||
p = p0w + head3[0];
|
p = p0w + head5[0];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
p = p0w + head3[1];
|
p = p0w + head5[1];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
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);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
// end edge circle
|
// end edge circle
|
||||||
for(int i=0; i<CIRCLE_SEGS; i++) {
|
for(int i=0; i<CIRCLE_SEGS; i++) {
|
||||||
p = p1w - gap + head[i];
|
p = p1w - head[i];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
}
|
}
|
||||||
p = p1w - gap + head[0];
|
p = p1w - head[0];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
// end edge triangle
|
// end edge triangle
|
||||||
triangle_head(-dir, -side, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, head3);
|
triangle_head(side, dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5);
|
||||||
p = p1w + head3[0];
|
p = p1w + head5[0];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
p = p1w + head3[1];
|
p = p1w + head5[1];
|
||||||
gl_Position = WIN2CLIP(p);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
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);
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
|
|
||||||
// stem
|
// stem
|
||||||
gl_Position = p0;
|
p = p0w + gap;
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
gl_Position = p1;
|
p = p1w - gap;
|
||||||
|
gl_Position = WIN2CLIP(p);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
EndPrimitive();
|
EndPrimitive();
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorate(self, context, obj):
|
def decorate(self, context, obj):
|
||||||
verts, _, _ = self.get_path_geom(obj, topo=False)
|
if obj.data.is_editmode:
|
||||||
self.draw_lines(context, obj, verts, [(0, 1)])
|
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):
|
class TextDecorator(BaseDecorator):
|
||||||
@@ -1422,7 +1460,7 @@ class DecorationsHandler:
|
|||||||
SectionLevelDecorator,
|
SectionLevelDecorator,
|
||||||
StairDecorator,
|
StairDecorator,
|
||||||
BreakDecorator,
|
BreakDecorator,
|
||||||
SectionViewDecorator,
|
SectionDecorator,
|
||||||
ElevationDecorator,
|
ElevationDecorator,
|
||||||
TextDecorator,
|
TextDecorator,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -260,14 +260,11 @@ def format_distance(value, isArea=False, hide_units=True):
|
|||||||
def get_active_drawing(scene):
|
def get_active_drawing(scene):
|
||||||
"""Get active drawing collection and camera"""
|
"""Get active drawing collection and camera"""
|
||||||
props = scene.DocProperties
|
props = scene.DocProperties
|
||||||
if props.active_drawing_index is None or len(props.drawings) == 0:
|
|
||||||
return None, None
|
|
||||||
try:
|
try:
|
||||||
drawing = props.active_drawing
|
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id))
|
||||||
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(drawing.ifc_definition_id))
|
|
||||||
return camera.users_collection[0], camera
|
return camera.users_collection[0], camera
|
||||||
except (KeyError, IndexError):
|
except:
|
||||||
raise RuntimeError("missing drawing collection")
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
def get_project_collection(scene):
|
def get_project_collection(scene):
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ class AddDrawing(bpy.types.Operator, Operator):
|
|||||||
target_view=self.props.target_view,
|
target_view=self.props.target_view,
|
||||||
location_hint=hint,
|
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):
|
class CreateDrawing(bpy.types.Operator):
|
||||||
@@ -777,12 +782,10 @@ class RemoveDrawing(bpy.types.Operator, Operator):
|
|||||||
bl_idname = "bim.remove_drawing"
|
bl_idname = "bim.remove_drawing"
|
||||||
bl_label = "Remove Drawing"
|
bl_label = "Remove Drawing"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
index: bpy.props.IntProperty()
|
drawing: bpy.props.IntProperty()
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.DocProperties
|
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||||
drawing = tool.Ifc.get().by_id(props.drawings[self.index].ifc_definition_id)
|
|
||||||
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=drawing)
|
|
||||||
|
|
||||||
|
|
||||||
class AddDrawingStyle(bpy.types.Operator):
|
class AddDrawingStyle(bpy.types.Operator):
|
||||||
|
|||||||
@@ -293,6 +293,7 @@ class DocProperties(PropertyGroup):
|
|||||||
)
|
)
|
||||||
location_hint: EnumProperty(items=get_location_hint, name="Location Hint")
|
location_hint: EnumProperty(items=get_location_hint, name="Location Hint")
|
||||||
drawings: CollectionProperty(name="Drawings", type=Drawing)
|
drawings: CollectionProperty(name="Drawings", type=Drawing)
|
||||||
|
active_drawing_id: IntProperty(name="Active Drawing Id")
|
||||||
active_drawing_index: IntProperty(name="Active Drawing Index")
|
active_drawing_index: IntProperty(name="Active Drawing Index")
|
||||||
current_drawing_index: IntProperty(name="Current Drawing Index")
|
current_drawing_index: IntProperty(name="Current Drawing Index")
|
||||||
schedules: CollectionProperty(name="Schedules", type=Schedule)
|
schedules: CollectionProperty(name="Schedules", type=Schedule)
|
||||||
@@ -312,10 +313,6 @@ class DocProperties(PropertyGroup):
|
|||||||
def active_schedule(self):
|
def active_schedule(self):
|
||||||
return self.schedules[self.active_schedule_index]
|
return self.schedules[self.active_schedule_index]
|
||||||
|
|
||||||
@property
|
|
||||||
def active_drawing(self):
|
|
||||||
return self.drawings[self.active_drawing_index]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_sheet(self):
|
def active_sheet(self):
|
||||||
return self.sheets[self.active_sheet_index]
|
return self.sheets[self.active_sheet_index]
|
||||||
|
|||||||
@@ -172,14 +172,15 @@ class BIM_PT_drawings(Panel):
|
|||||||
|
|
||||||
if self.props.drawings:
|
if self.props.drawings:
|
||||||
if self.props.active_drawing_index < len(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 = self.layout.row(align=True)
|
||||||
row.alignment = "RIGHT"
|
row.alignment = "RIGHT"
|
||||||
op = row.operator("bim.open_view", icon="URL", text="")
|
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 = 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.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(
|
self.layout.template_list(
|
||||||
"BIM_UL_drawinglist", "", self.props, "drawings", self.props, "active_drawing_index"
|
"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)
|
target_view = psets.get("EPset_Drawing", {}).get("TargetView", None)
|
||||||
if target_view == "ELEVATION_VIEW":
|
if target_view == "ELEVATION_VIEW":
|
||||||
return cls.generate_elevation_reference_annotation(drawing, reference_element, context)
|
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
|
@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 = tool.Ifc.get_object(reference_element)
|
||||||
reference_obj.matrix_world
|
reference_obj.matrix_world
|
||||||
camera = tool.Ifc.get_object(drawing)
|
camera = tool.Ifc.get_object(drawing)
|
||||||
|
bounds = helper.ortho_view_frame(camera.data) if camera.data.type == "ORTHO" else None
|
||||||
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))
|
|
||||||
|
|
||||||
def to_camera_coords(camera, reference_obj):
|
def to_camera_coords(camera, reference_obj):
|
||||||
mat = reference_obj.matrix_world.copy()
|
mat = reference_obj.matrix_world.copy()
|
||||||
@@ -476,14 +431,90 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
mat[0][3] = xyz[0]
|
mat[0][3] = xyz[0]
|
||||||
mat[1][3] = xyz[1]
|
mat[1][3] = xyz[1]
|
||||||
mat[2][3] = xyz[2]
|
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
|
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||||
mat[0][3] += annotation_offset[0]
|
mat[0][3] += annotation_offset[0]
|
||||||
mat[1][3] += annotation_offset[1]
|
mat[1][3] += annotation_offset[1]
|
||||||
mat[2][3] += annotation_offset[2]
|
mat[2][3] += annotation_offset[2]
|
||||||
return mat
|
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 = bpy.data.objects.new(reference_obj.name, None)
|
||||||
obj.empty_display_size = 0.1
|
obj.empty_display_size = 0.1
|
||||||
obj.matrix_world = to_camera_coords(camera, reference_obj)
|
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):
|
def to_camera_coords(obj, mesh):
|
||||||
mesh.transform(camera.matrix_world.inverted() @ obj.matrix_world)
|
mesh.transform(camera.matrix_world.inverted() @ obj.matrix_world)
|
||||||
obj.matrix_world = camera.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
|
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||||
obj.matrix_world[0][3] += annotation_offset[0]
|
obj.matrix_world[0][3] += annotation_offset[0]
|
||||||
obj.matrix_world[1][3] += annotation_offset[1]
|
obj.matrix_world[1][3] += annotation_offset[1]
|
||||||
@@ -585,6 +616,57 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
)
|
)
|
||||||
return element
|
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
|
@classmethod
|
||||||
def sync_object_representation(cls, obj):
|
def sync_object_representation(cls, obj):
|
||||||
bpy.ops.bim.update_representation(obj=obj.name)
|
bpy.ops.bim.update_representation(obj=obj.name)
|
||||||
|
|||||||
Reference in New Issue
Block a user