mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-03 13:28:19 +00:00
decorator classes for Dim/Eq/Leader/Stair/Hidden
This commit is contained in:
@@ -14,7 +14,45 @@ from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertForma
|
|||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
|
|
||||||
class ViewDecorator(object):
|
class Shaders:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BaseDecorator():
|
||||||
|
# base name of objects to decorate
|
||||||
|
basename = "IfcAnnotation/Something"
|
||||||
|
|
||||||
|
VERT_GLSL = """
|
||||||
|
uniform mat4 viewMatrix;
|
||||||
|
in vec3 pos;
|
||||||
|
out vec4 gl_Position;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = viewMatrix * vec4(pos, 1.0);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
GEOM_GLSL = """
|
||||||
|
layout(lines) in;
|
||||||
|
layout(line_strip, max_vertices=2) out;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||||
|
gl_Position = p0;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p1;
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
FRAG_GLSL = """
|
||||||
|
uniform vec3 color;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
fragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
# class var for single handler
|
# class var for single handler
|
||||||
installed = None
|
installed = None
|
||||||
|
|
||||||
@@ -31,97 +69,9 @@ class ViewDecorator(object):
|
|||||||
pass
|
pass
|
||||||
cls.installed = None
|
cls.installed = None
|
||||||
|
|
||||||
|
|
||||||
class DimensionDecorator(ViewDecorator):
|
|
||||||
"""Decorates dimension curves
|
|
||||||
- outlines each segment with an arrow
|
|
||||||
- puts metric text next to each segment
|
|
||||||
"""
|
|
||||||
|
|
||||||
VERT_GLSL = """
|
|
||||||
uniform mat4 viewMatrix;
|
|
||||||
in vec3 pos;
|
|
||||||
out vec4 gl_Position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = viewMatrix * vec4(pos, 1.0);
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
GEOM_GLSL = """
|
|
||||||
layout(lines) in;
|
|
||||||
layout(line_strip, max_vertices=10) out;
|
|
||||||
|
|
||||||
uniform float windowW;
|
|
||||||
uniform float windowH;
|
|
||||||
uniform float angle;
|
|
||||||
uniform float length;
|
|
||||||
uniform float aspect;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
/** generates arrows from lines */
|
|
||||||
|
|
||||||
// converting NDC to/from window coords
|
|
||||||
float hw = windowW/2, hh= windowH/2;
|
|
||||||
mat4 windowMatrix = mat4(hw, 0, 0, 0, 0, hh, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
|
||||||
mat4 unwindowMatrix = inverse(windowMatrix);
|
|
||||||
|
|
||||||
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
|
||||||
|
|
||||||
vec4 p0w = windowMatrix * (p0 / p0.w), p1w = windowMatrix * (p1 / p1.w);
|
|
||||||
|
|
||||||
// arrow head of specified length in direction of a segment
|
|
||||||
// rotating the head left/right to form triangle
|
|
||||||
vec4 head = normalize(p1w - p0w) * length;
|
|
||||||
float c = cos(angle), s = sin(angle);
|
|
||||||
vec4 head_a = mat4( c, -s, 0, 0, +s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * head;
|
|
||||||
vec4 head_b = mat4( c, +s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * head;
|
|
||||||
|
|
||||||
// projecting back to NDC
|
|
||||||
vec4 head_ndc = unwindowMatrix * head;
|
|
||||||
vec4 head_a_ndc = unwindowMatrix * head_a;
|
|
||||||
vec4 head_b_ndc = unwindowMatrix * head_b;
|
|
||||||
|
|
||||||
gl_Position = p0 + head_ndc * p0.w;
|
|
||||||
EmitVertex();
|
|
||||||
gl_Position = p1 - head_ndc * p1.w;
|
|
||||||
EmitVertex();
|
|
||||||
EndPrimitive();
|
|
||||||
|
|
||||||
gl_Position = p0;
|
|
||||||
EmitVertex();
|
|
||||||
gl_Position = p0 + head_a_ndc * p0.w;
|
|
||||||
EmitVertex();
|
|
||||||
gl_Position = p0 + head_b_ndc * p0.w;
|
|
||||||
EmitVertex();
|
|
||||||
gl_Position = p0;
|
|
||||||
EmitVertex();
|
|
||||||
EndPrimitive();
|
|
||||||
|
|
||||||
gl_Position = p1;
|
|
||||||
EmitVertex();
|
|
||||||
gl_Position = p1 - head_b_ndc * p1.w;
|
|
||||||
EmitVertex();
|
|
||||||
gl_Position = p1 - head_a_ndc * p1.w;
|
|
||||||
EmitVertex();
|
|
||||||
gl_Position = p1;
|
|
||||||
EmitVertex();
|
|
||||||
EndPrimitive();
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
FRAG_GLSL = """
|
|
||||||
uniform vec3 color;
|
|
||||||
out vec4 fragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
fragColor = vec4(color, 1.0);
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, props, context):
|
def __init__(self, props, context):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.props = props
|
self.props = props
|
||||||
self.font_id = 0 # TODO: take font from styles
|
|
||||||
self.dpi = context.preferences.system.dpi
|
|
||||||
self.shader = self.create_shader()
|
self.shader = self.create_shader()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -132,45 +82,149 @@ class DimensionDecorator(ViewDecorator):
|
|||||||
# get active drawing, if any
|
# get active drawing, if any
|
||||||
if self.props.active_drawing_index is None or len(self.props.drawings) == 0:
|
if self.props.active_drawing_index is None or len(self.props.drawings) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
objects = list(self.get_objects())
|
||||||
|
if not objects:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.decorate(objects)
|
||||||
|
|
||||||
|
def get_objects(self):
|
||||||
|
"""find relevant objects
|
||||||
|
using class.basename
|
||||||
|
|
||||||
|
returns: iterable of blender objects
|
||||||
|
"""
|
||||||
drawing = self.props.drawings[self.props.active_drawing_index]
|
drawing = self.props.drawings[self.props.active_drawing_index]
|
||||||
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
|
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
|
||||||
|
return filter(lambda o: self.basename in o.name, collection.objects)
|
||||||
|
|
||||||
segments = self.get_segments(self.get_curves(collection, "IfcAnnotation/Dimension"))
|
def decorate(self, objects):
|
||||||
|
"""perform actuall drawing stuff"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
self.draw_arrows(segments)
|
def flat_points(self, segments):
|
||||||
for segm in segments:
|
"""flatten segments into list of their points
|
||||||
self.draw_label(segm, f"{segm[2]:.2f}")
|
returns: iterable of points as coords tuples
|
||||||
|
|
||||||
# segments = self.get_segments(self.get_curves(collection, "IfcAnnotation/Equal"))
|
|
||||||
|
|
||||||
# self.draw_arrows(segments)
|
|
||||||
# for segm in segments:
|
|
||||||
# self.draw_label(segm, "EQ")
|
|
||||||
|
|
||||||
def get_curves(self, collection, basename):
|
|
||||||
return list(filter(lambda o: basename in o.name, collection.objects))
|
|
||||||
|
|
||||||
def get_segments(self, curves):
|
|
||||||
return list(chain.from_iterable(self.iter_segments(curve) for curve in curves))
|
|
||||||
|
|
||||||
def iter_segments(self, curve):
|
|
||||||
"""Yields each segment converted to world coords
|
|
||||||
(v0, v1, length)
|
|
||||||
"""
|
"""
|
||||||
for spline in curve.data.splines:
|
def coords(v):
|
||||||
|
return tuple(v)[:3]
|
||||||
|
return reduce(lambda points, segm: points + [coords(segm[0]), coords(segm[1])], segments, [])
|
||||||
|
|
||||||
|
|
||||||
|
class PathDecorator:
|
||||||
|
def get_segments(self, object):
|
||||||
|
"""extract segments from curve object"""
|
||||||
|
for spline in object.data.splines:
|
||||||
spline_points = spline.bezier_points if spline.bezier_points else spline.points
|
spline_points = spline.bezier_points if spline.bezier_points else spline.points
|
||||||
points = [curve.matrix_world @ p.co for p in spline_points]
|
points = [object.matrix_world @ p.co for p in spline_points]
|
||||||
for i in range(len(points)-1):
|
for i in range(len(points)-1):
|
||||||
p0 = points[i]
|
p0 = points[i]
|
||||||
p1 = points[i+1]
|
p1 = points[i+1]
|
||||||
length = (p1 - p0).length
|
yield (p0, p1)
|
||||||
yield (p0, p1, length)
|
|
||||||
|
|
||||||
|
class MeshDecorator:
|
||||||
|
def get_segments(self, object):
|
||||||
|
"""extract segments from mesh object"""
|
||||||
|
for edge in object.data.edges:
|
||||||
|
p0 = object.data.vertices[edge.vertices[0]].co
|
||||||
|
p1 = object.data.vertices[edge.vertices[1]].co
|
||||||
|
yield (p0, p1)
|
||||||
|
|
||||||
|
|
||||||
|
class DimensionDecorator(PathDecorator, BaseDecorator):
|
||||||
|
"""Decorator for dimension objects
|
||||||
|
- outlines each segment
|
||||||
|
- augments with arrows on both sides
|
||||||
|
- puts metric text next to each segment
|
||||||
|
"""
|
||||||
|
basename = "IfcAnnotation/Dimension"
|
||||||
|
installed = None
|
||||||
|
|
||||||
|
GEOM_GLSL = """
|
||||||
|
layout(lines) in;
|
||||||
|
layout(line_strip, max_vertices=10) out;
|
||||||
|
|
||||||
|
uniform float windowW;
|
||||||
|
uniform float windowH;
|
||||||
|
uniform float arrow_length;
|
||||||
|
uniform float arrow_angle;
|
||||||
|
|
||||||
|
void arrow_head(in vec2 p0, in vec2 p1, out vec2 head[3]) {
|
||||||
|
vec2 nose = normalize(p1 - p0) * arrow_length;
|
||||||
|
float c = cos(arrow_angle), s = sin(arrow_angle);
|
||||||
|
head[0] = nose;
|
||||||
|
head[1] = mat2( c, -s, +s, c) * nose;
|
||||||
|
head[2] = mat2( c, +s, -s, c) * nose;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
/** generates arrows from lines */
|
||||||
|
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||||
|
|
||||||
|
// converting NDC to/from window coords
|
||||||
|
float hw = windowW/2, hh= windowH/2;
|
||||||
|
mat4 windowMatrix = mat4(hw, 0, 0, 0, 0, hh, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||||
|
mat4 unwindowMatrix = inverse(windowMatrix);
|
||||||
|
|
||||||
|
vec2 head[3];
|
||||||
|
vec4 head_ndc[3];
|
||||||
|
|
||||||
|
vec4 p0w = windowMatrix * (p0 / p0.w), p1w = windowMatrix * (p1 / p1.w);
|
||||||
|
arrow_head(p0w.xy, p1w.xy, head);
|
||||||
|
for(int i=0; i<3; i++) head_ndc[i] = unwindowMatrix * vec4(head[i], 0, 0);
|
||||||
|
|
||||||
|
gl_Position = p0 + head_ndc[0] * p0.w;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p1 - head_ndc[0] * p1.w;
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
gl_Position = p0;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p0 + head_ndc[1] * p0.w;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p0 + head_ndc[2] * p0.w;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p0;
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
gl_Position = p1;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p1 - head_ndc[1] * p1.w;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p1 - head_ndc[2] * p1.w;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p1;
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, props, context):
|
||||||
|
super().__init__(props, context)
|
||||||
|
self.font_id = 0
|
||||||
|
self.dpi = context.preferences.system.dpi
|
||||||
|
|
||||||
|
def decorate(self, objects):
|
||||||
|
segments = list(chain.from_iterable(self.get_segments(obj) for obj in objects))
|
||||||
|
if not segments:
|
||||||
|
return
|
||||||
|
|
||||||
|
for segm in segments:
|
||||||
|
length = (segm[1] - segm[0]).length
|
||||||
|
text = f"{length:.2f}"
|
||||||
|
self.draw_label(segm, text)
|
||||||
|
|
||||||
|
self.draw_arrows(segments)
|
||||||
|
|
||||||
def draw_label(self, segm, text):
|
def draw_label(self, segm, text):
|
||||||
"""Draw text of segment length
|
"""Draw text of segment length
|
||||||
aligned and centered at segment middle
|
aligned and centered at segment middle
|
||||||
"""
|
"""
|
||||||
p0, p1, _ = segm
|
p0, p1 = segm
|
||||||
|
|
||||||
# convert to view coords
|
# convert to view coords
|
||||||
region = self.context.region
|
region = self.context.region
|
||||||
@@ -210,23 +264,91 @@ class DimensionDecorator(ViewDecorator):
|
|||||||
blf.disable(self.font_id, blf.ROTATION)
|
blf.disable(self.font_id, blf.ROTATION)
|
||||||
|
|
||||||
def draw_arrows(self, segments):
|
def draw_arrows(self, segments):
|
||||||
def coords(segm):
|
|
||||||
return [(segm[0].x, segm[0].y, segm[0].z),
|
|
||||||
(segm[1].x, segm[1].y, segm[1].z)]
|
|
||||||
points = list(reduce(lambda points, segm: points + coords(segm),
|
|
||||||
segments, []))
|
|
||||||
batch = batch_for_shader(self.shader, 'LINES', {'pos': points})
|
|
||||||
self.shader.bind()
|
|
||||||
|
|
||||||
region = self.context.region
|
region = self.context.region
|
||||||
region3d = self.context.region_data
|
region3d = self.context.region_data
|
||||||
|
points = self.flat_points(segments)
|
||||||
|
|
||||||
|
batch = batch_for_shader(self.shader, 'LINES', {'pos': points})
|
||||||
|
self.shader.bind()
|
||||||
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
|
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
|
||||||
self.shader.uniform_float("windowW", region.width)
|
self.shader.uniform_float("windowW", region.width)
|
||||||
self.shader.uniform_float("windowH", region.height)
|
self.shader.uniform_float("windowH", region.height)
|
||||||
|
# # TODO: get everything from styles
|
||||||
# TODO: get everything from styles
|
self.shader.uniform_float('color', (1.0, 1.0, 1.0))
|
||||||
|
self.shader.uniform_float('arrow_angle', math.pi / 12)
|
||||||
|
self.shader.uniform_float('arrow_length', 16)
|
||||||
|
batch.draw(self.shader)
|
||||||
|
|
||||||
|
|
||||||
|
class EqualityDecorator(DimensionDecorator):
|
||||||
|
"""Decorator for equality objects
|
||||||
|
- outlines each segment
|
||||||
|
- augments with arrows on both sides
|
||||||
|
- puts 'EQ' label
|
||||||
|
"""
|
||||||
|
basename = "IfcAnnotation/Equal"
|
||||||
|
installed = None
|
||||||
|
|
||||||
|
def decorate(self, objects):
|
||||||
|
segments = list(chain.from_iterable(self.get_segments(obj) for obj in objects))
|
||||||
|
if not segments:
|
||||||
|
return
|
||||||
|
|
||||||
|
for segm in segments:
|
||||||
|
self.draw_label(segm, 'EQ')
|
||||||
|
|
||||||
|
self.draw_arrows(segments)
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderDecorator(PathDecorator, BaseDecorator):
|
||||||
|
basename = "IfcAnnotation/Leader"
|
||||||
|
installed = None
|
||||||
|
|
||||||
|
def decorate(self, objects):
|
||||||
|
segments = list(chain.from_iterable(self.get_segments(obj) for obj in objects))
|
||||||
|
if not segments:
|
||||||
|
return
|
||||||
|
|
||||||
|
region3d = self.context.region_data
|
||||||
|
points = self.flat_points(segments)
|
||||||
|
batch = batch_for_shader(self.shader, 'LINES', {'pos': points})
|
||||||
|
self.shader.bind()
|
||||||
|
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
|
||||||
|
self.shader.uniform_float('color', (1.0, 1.0, 1.0))
|
||||||
|
batch.draw(self.shader)
|
||||||
|
|
||||||
|
|
||||||
|
class StairDecorator(PathDecorator, BaseDecorator):
|
||||||
|
basename = "IfcAnnotation/Stair"
|
||||||
|
installed = None
|
||||||
|
|
||||||
|
def decorate(self, objects):
|
||||||
|
segments = list(chain.from_iterable(self.get_segments(obj) for obj in objects))
|
||||||
|
if not segments:
|
||||||
|
return
|
||||||
|
|
||||||
|
region3d = self.context.region_data
|
||||||
|
points = self.flat_points(segments)
|
||||||
|
batch = batch_for_shader(self.shader, 'LINES', {'pos': points})
|
||||||
|
self.shader.bind()
|
||||||
|
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
|
||||||
|
self.shader.uniform_float('color', (1.0, 1.0, 1.0))
|
||||||
|
batch.draw(self.shader)
|
||||||
|
|
||||||
|
|
||||||
|
class HiddenDecorator(MeshDecorator, BaseDecorator):
|
||||||
|
basename = "IfcAnnotation/Hidden"
|
||||||
|
installed = None
|
||||||
|
|
||||||
|
def decorate(self, objects):
|
||||||
|
segments = list(chain.from_iterable(self.get_segments(obj) for obj in objects))
|
||||||
|
if not segments:
|
||||||
|
return
|
||||||
|
|
||||||
|
region3d = self.context.region_data
|
||||||
|
points = self.flat_points(segments)
|
||||||
|
batch = batch_for_shader(self.shader, 'LINES', {'pos': points})
|
||||||
|
self.shader.bind()
|
||||||
|
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
|
||||||
self.shader.uniform_float('color', (1.0, 1.0, 1.0))
|
self.shader.uniform_float('color', (1.0, 1.0, 1.0))
|
||||||
self.shader.uniform_float('angle', math.pi / 12)
|
|
||||||
self.shader.uniform_float('length', 16)
|
|
||||||
batch.draw(self.shader)
|
batch.draw(self.shader)
|
||||||
|
|||||||
@@ -362,8 +362,16 @@ def toggleDecorations(self, context):
|
|||||||
toggle = self.should_draw_decorations
|
toggle = self.should_draw_decorations
|
||||||
if toggle:
|
if toggle:
|
||||||
decoration.DimensionDecorator.install(self, context)
|
decoration.DimensionDecorator.install(self, context)
|
||||||
|
decoration.EqualityDecorator.install(self, context)
|
||||||
|
decoration.LeaderDecorator.install(self, context)
|
||||||
|
decoration.StairDecorator.install(self, context)
|
||||||
|
decoration.HiddenDecorator.install(self, context)
|
||||||
else:
|
else:
|
||||||
decoration.DimensionDecorator.uninstall()
|
decoration.DimensionDecorator.uninstall()
|
||||||
|
decoration.EqualityDecorator.uninstall()
|
||||||
|
decoration.LeaderDecorator.uninstall()
|
||||||
|
decoration.StairDecorator.uninstall()
|
||||||
|
decoration.HiddenDecorator.uninstall()
|
||||||
|
|
||||||
|
|
||||||
def getScenarios(self, context):
|
def getScenarios(self, context):
|
||||||
|
|||||||
Reference in New Issue
Block a user