Files
IfcOpenShell/src/ifcblenderexport/blenderbim/bim/decoration.py
T
2020-12-31 19:56:55 +07:00

1045 lines
28 KiB
Python

"""Viewport decorations"""
import math
from functools import reduce
from itertools import chain
from bpy.types import SpaceView3D
from mathutils import Vector, Matrix
import bpy
import bmesh
import blf
from bpy_extras.view3d_utils import location_3d_to_region_2d
import gpu
import bgl
from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertFormat
from gpu_extras.batch import batch_for_shader
class BaseDecorator():
# base name of objects to decorate
basename = "IfcAnnotation/Something"
DEF_GLSL = """
#define PI 3.141592653589793
#define COLOR vec4({color[0]}, {color[1]}, {color[2]}, {color[3]})
#define MAX_POINTS 64
#define CIRCLE_SEGS 24
"""
LIB_GLSL = """
#define matCLIP2WIN() vec4(winsize.x/2, winsize.y/2, 1, 1)
#define matWIN2CLIP() vec4(2/winsize.x, 2/winsize.y, 1, 1)
#define CLIP2WIN(v) (clip2win * v / v.w)
#define WIN2CLIP(v) (win2clip * v * v.w)
void arrow_head(in vec4 dir, in float size, in float angle, out vec4 head[3]) {
vec4 nose = dir * size;
float c = cos(angle), s = sin(angle);
head[0] = nose;
head[1] = vec4(mat2(c, -s, +s, c) * nose.xy, 0, 0);
head[2] = vec4(mat2(c, +s, -s, c) * nose.xy, 0, 0);
}
void circle_head(in float size, out vec4 head[CIRCLE_SEGS]) {
float angle_d = PI * 2 / CIRCLE_SEGS;
for(int i = 0; i<CIRCLE_SEGS; i++) {
float angle = angle_d * i;
head[i] = vec4(cos(angle), sin(angle), 0, 0) * size;
}
}
void cross_head(in vec4 dir, in float size, out vec4 head[3]) {
vec4 nose = dir * size;
float c = cos(PI/2), s = sin(PI/2);
head[0] = nose;
head[1] = vec4(mat2(c, -s, +s, c) * nose.xy, 0, 0);
head[2] = vec4(mat2(c, +s, -s, c) * nose.xy, 0, 0);
}
"""
VERT_GLSL = """
uniform mat4 viewMatrix;
in vec3 pos;
in uint topo;
out vec4 gl_Position;
out uint type;
void main() {
gl_Position = viewMatrix * vec4(pos, 1.0);
type = topo;
}
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=2) out;
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
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 * 16.0;
vec4 p;
p = p0w + gap;
gl_Position = WIN2CLIP(p);
EmitVertex();
p = p1w - gap;
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
}
"""
FRAG_GLSL = """
out vec4 fragColor;
void main() {
fragColor = COLOR;
}
"""
# class var for single handler
installed = None
@classmethod
def install(cls, *args, **kwargs):
if cls.installed:
cls.uninstall()
handler = cls(*args, **kwargs)
cls.installed = SpaceView3D.draw_handler_add(handler, (), 'WINDOW', 'POST_PIXEL')
@classmethod
def uninstall(cls):
try:
SpaceView3D.draw_handler_remove(cls.installed, 'WINDOW')
except ValueError:
pass
cls.installed = None
def __init__(self, props, context):
self.context = context
self.props = props
self.shader = self.create_shader()
self.font_id = 0
self.font_size = 16
self.dpi = context.preferences.system.dpi
def create_shader(self):
defines = self.DEF_GLSL.format(color=self.props.decorations_colour)
# NB: libcode param doesn't work
return GPUShader(vertexcode=self.VERT_GLSL,
fragcode=self.FRAG_GLSL,
geocode=self.LIB_GLSL + self.GEOM_GLSL,
defines=defines)
def __call__(self):
if self.props.active_drawing_index is None or len(self.props.drawings) == 0:
return
for obj in self.get_objects():
self.decorate(obj)
def get_objects(self):
"""find relevant objects
using class.basename
returns: iterable of blender objects
"""
drawing = self.props.drawings[self.props.active_drawing_index]
collection = bpy.data.collections.get("IfcGroup/" + drawing.name)
return filter(lambda o: self.basename in o.name, collection.objects)
def get_path_geom(self, obj, topo=True):
"""Parses path geometry into line segments
Args:
obj: Blender object with data of type Curve
topo: bool; if types of vertices are needed
Returns:
vertices: 3-tuples of coords
indices: 2-tuples of each segment verices' indices
topology: types of vertices
0: internal
1: beginning
2: ending
"""
vertices = []
indices = []
topology = []
idx = 0
for spline in obj.data.splines:
spline_points = spline.bezier_points if spline.bezier_points else spline.points
if len(spline_points) < 2:
continue
points = [obj.matrix_world @ p.co for p in spline_points]
cnt = len(points)
vertices.extend(p[:3] for p in points)
if topo:
topology.append(1)
topology.extend([0] * max(0, cnt - 2))
topology.append(2)
indices.extend((idx+i, idx+i+1) for i in range(cnt-1))
idx += cnt
return vertices, indices, topology
def get_mesh_geom(self, obj):
"""Parses mesh geometry into line segments
Args:
obj: Blender object with data of type Mesh
Returns:
vertices: 3-tuples of coords
indices: 2-tuples of each segment verices' indices
"""
vertices = [obj.matrix_world @ v.co for v in obj.data.vertices]
indices = [e.vertices for e in obj.data.edges]
return vertices, indices
def get_editmesh_geom(self, obj):
"""Parses editmode mesh geometry into line segments
"""
mesh = bmesh.from_edit_mesh(obj.data)
vertices = []
indices = []
idx = 0
for edge in mesh.edges:
vertices.extend(edge.verts)
indices.append((idx, idx+1))
idx += 2
vertices = [obj.matrix_world @ v.co for v in vertices]
return vertices, indices
def decorate(self, object):
"""perform actuall drawing stuff"""
raise NotImplementedError()
def draw_lines(self, obj, vertices, indices, topology=None):
region = self.context.region
region3d = self.context.region_data
fmt = GPUVertFormat()
fmt.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
if topology:
fmt.attr_add(id="topo", comp_type='U8', len=1, fetch_mode='INT')
vbo = GPUVertBuf(len=len(vertices), format=fmt)
vbo.attr_fill(id="pos", data=vertices)
if topology:
vbo.attr_fill(id="topo", data=topology)
ibo = GPUIndexBuf(type='LINES', seq=indices)
batch = GPUBatch(type='LINES', buf=vbo, elem=ibo)
self.shader.bind()
self.shader.uniform_float
self.shader.uniform_float("viewMatrix", region3d.perspective_matrix)
self.shader.uniform_float("winsize", (region.width, region.height))
bgl.glEnable(bgl.GL_LINE_SMOOTH)
bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST)
bgl.glEnable(bgl.GL_BLEND)
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
batch.draw(self.shader)
def draw_label(self, text, pos, dir, gap=4, center=True, vcenter=False):
"""Draw text label
Args:
pos: bottom-center
aligned and centered at segment middle
"""
ang = -Vector((1, 0)).angle_signed(dir)
cos = math.cos(ang)
sin = math.sin(ang)
blf.size(self.font_id, self.font_size, self.dpi)
w, h = 0, 0
if center or vcenter:
w, h = blf.dimensions(self.font_id, text)
if center:
# horizontal centering
pos -= Vector((cos, sin)) * w * 0.5
if vcenter:
# vertical centering
pos -= Vector((-sin, cos)) * h * 0.5
if gap:
# side-shifting
pos += Vector((-sin, cos)) * gap
blf.enable(self.font_id, blf.ROTATION)
blf.position(self.font_id, pos.x, pos.y, 0)
blf.rotation(self.font_id, ang)
blf.color(self.font_id, *self.props.decorations_colour)
blf.draw(self.font_id, text)
blf.disable(self.font_id, blf.ROTATION)
def format_value(self, value):
unit_system = bpy.context.scene.unit_settings.system
if unit_system == 'IMPERIAL':
precision = bpy.context.scene.BIMProperties.imperial_precision
if precision == "NONE":
precision = 256
elif precision == "1":
precision = 1
elif "/" in precision:
precision = int(precision.split("/")[1])
elif unit_system == 'METRIC':
precision = 3
else:
return
return bpy.utils.units.to_string(unit_system, 'LENGTH', value, precision, split_unit=True)
class DimensionDecorator(BaseDecorator):
"""Decorator for dimension objects
- each edge of a segment with arrow
- puts metric text next to each segment
"""
basename = "IfcAnnotation/Dimension"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define ARROW_ANGLE PI / 12.0
#define ARROW_SIZE 16.0
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
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 p;
vec4 head[3];
arrow_head(dir, ARROW_SIZE, ARROW_ANGLE, head);
// start edge arrow
gl_Position = p0;
EmitVertex();
p = p0w + head[1];
gl_Position = WIN2CLIP(p);
EmitVertex();
p = p0w + head[2];
gl_Position = WIN2CLIP(p);
EmitVertex();
gl_Position = p0;
EmitVertex();
EndPrimitive();
// end edge arrow
gl_Position = p1;
EmitVertex();
p = p1w - head[1];
gl_Position = WIN2CLIP(p);
EmitVertex();
p = p1w - head[2];
gl_Position = WIN2CLIP(p);
EmitVertex();
gl_Position = p1;
EmitVertex();
EndPrimitive();
// stem, with gaps for arrows
p = p0w + head[0];
gl_Position = WIN2CLIP(p);
EmitVertex();
p = p1w - head[0];
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
}
"""
def decorate(self, obj):
verts, idxs, _ = self.get_path_geom(obj, topo=False)
self.draw_lines(obj, verts, idxs)
self.draw_labels(obj, verts, idxs)
def draw_labels(self, obj, vertices, indices):
region = self.context.region
region3d = self.context.region_data
for i0, i1 in indices:
v0 = Vector(vertices[i0])
v1 = Vector(vertices[i1])
p0 = location_3d_to_region_2d(region, region3d, v0)
p1 = location_3d_to_region_2d(region, region3d, v1)
dir = p1 - p0
if dir.length < 1:
continue
length = (v1 - v0).length
text = self.format_value(length)
self.draw_label(text, p0 + (dir) * .5, dir)
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 draw_labels(self, obj, vertices, indices):
region = self.context.region
region3d = self.context.region_data
for i0, i1 in indices:
v0 = Vector(vertices[i0])
v1 = Vector(vertices[i1])
p0 = location_3d_to_region_2d(region, region3d, v0)
p1 = location_3d_to_region_2d(region, region3d, v1)
dir = p1 - p0
if dir.length < 1:
continue
self.draw_label("EQ", p0 + (dir) * .5, dir)
class LeaderDecorator(BaseDecorator):
"""Decorating stairs
- head point with arrow
- middle points w/out decorations
"""
basename = "IfcAnnotation/Leader"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define ARROW_ANGLE PI / 12.0
#define ARROW_SIZE 16.0
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
in uint type[];
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
uint t0 = type[0], t1 = type[1];
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
vec4 edge = p1w - p0w, dir = normalize(edge);
vec4 gap1 = vec4(0);
vec4 p;
// end edge arrow for last segment
if (t1 == 2u) {
vec4 head[3];
arrow_head(dir, ARROW_SIZE, ARROW_ANGLE, head);
gl_Position = p1;
EmitVertex();
p = p1w - head[1];
gl_Position = WIN2CLIP(p);
EmitVertex();
p = p1w - head[2];
gl_Position = WIN2CLIP(p);
EmitVertex();
gl_Position = p1;
EmitVertex();
EndPrimitive();
gap1 = dir * ARROW_SIZE;
}
// stem, adjusted for and arrow
gl_Position = p0;
EmitVertex();
p = p1w - gap1;
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
}
"""
def decorate(self, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
class StairDecorator(BaseDecorator):
"""Decorating stairs
- head point with arrow
- tail point with circle
- middle points w/out decorations
"""
basename = "IfcAnnotation/Stair"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 8.0
#define ARROW_ANGLE PI / 3.0
#define ARROW_SIZE 24.0
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
in uint type[];
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
uint t0 = type[0], t1 = type[1];
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
vec4 edge = p1w - p0w, dir = normalize(edge);
vec4 gap0 = vec4(0), gap1 = vec4(0);
vec4 p;
// start edge circle for first segment
if (t0 == 1u) {
vec4 head[CIRCLE_SEGS];
circle_head(CIRCLE_SIZE, head);
for(int i=0; i<CIRCLE_SEGS; i++) {
p = p0w + head[i];
gl_Position = WIN2CLIP(p);
EmitVertex();
}
p = p0w + head[0];
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
gap0 = dir * CIRCLE_SIZE;
}
// end edge arrow for last segment
if (t1 == 2u) {
vec4 head[3];
arrow_head(dir, ARROW_SIZE, ARROW_ANGLE, head);
p = p1w - head[1];
gl_Position = WIN2CLIP(p);
EmitVertex();
gl_Position = p1;
EmitVertex();
p = p1w - head[2];
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
}
// stem, with gaps for edge decoration
p = p0w + gap0;
gl_Position = WIN2CLIP(p);
EmitVertex();
gl_Position = p1;
EmitVertex();
EndPrimitive();
}
"""
def decorate(self, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
class HiddenDecorator(BaseDecorator):
basename = "IfcAnnotation/Hidden"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define DASH_SIZE 16.0
#define DASH_PATTERN 0x0000FFFFU
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
out float dist; // distance from starging point along segment
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
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 * 1;
vec4 p;
// NB: something should be used to affect position, otherwise compiler eliminates winsize
dist = 0;
p = p0w + gap;
gl_Position = WIN2CLIP(p);
EmitVertex();
dist = length(edge);
p = p1w - gap;
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
}
"""
FRAG_GLSL = """
in vec2 gl_FragCoord;
in float dist;
out vec4 fragColor;
void main() {
uint bit = uint(fract(dist / DASH_SIZE) * 32);
if ((DASH_PATTERN & (1U<<bit)) == 0U) discard;
fragColor = COLOR;
}
"""
def decorate(self, obj):
if obj.data.is_editmode:
verts, idxs = self.get_editmesh_geom(obj)
else:
verts, idxs = self.get_mesh_geom(obj)
self.draw_lines(obj, verts, idxs)
class MiscDecorator(HiddenDecorator):
basename = "IfcAnnotation/Misc"
installed = None
FRAG_GLSL = BaseDecorator.FRAG_GLSL
class LevelDecorator(BaseDecorator):
def get_splines(self, obj):
"""Iterates through splines
Args:
obj: Blender object with Curve data
Yields:
verts: points of each spline, world coords
"""
for spline in obj.data.splines:
spline_points = spline.bezier_points if spline.bezier_points else spline.points
if len(spline_points) < 2:
continue
yield [obj.matrix_world @ p.co for p in spline_points]
def decorate(self, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(obj, verts, idxs, topo)
splines = self.get_splines(obj)
self.draw_labels(obj, splines)
class PlanDecorator(LevelDecorator):
basename = "IfcAnnotation/Plan Level"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 8.0
#define CROSS_SIZE 16.0
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
in uint type[];
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
uint t0 = type[0], t1 = type[1];
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
vec4 edge = p1w - p0w, dir = normalize(edge);
vec4 gap1 = vec4(0);
vec4 p;
// end edge cross+circle for last segment
if (t1 == 2u) {
vec4 head_o[CIRCLE_SEGS];
circle_head(CIRCLE_SIZE, head_o);
for(int i=0; i<CIRCLE_SEGS; i++) {
p = p1w + head_o[i];
gl_Position = WIN2CLIP(p);
EmitVertex();
}
p = p1w + head_o[0];
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
vec4 head_x[3];
cross_head(dir, CROSS_SIZE, head_x);
p = p1w + head_x[1];
gl_Position = WIN2CLIP(p);
EmitVertex();
p = p1w + head_x[2];
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
gap1 = head_x[0];
}
// stem, adjusted for and arrow
gl_Position = p0;
EmitVertex();
p = p1w + gap1;
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
}
"""
def draw_labels(self, obj, splines):
region = self.context.region
region3d = self.context.region_data
for verts in splines:
v0 = verts[0]
v1 = verts[1]
p0 = location_3d_to_region_2d(region, region3d, v0)
p1 = location_3d_to_region_2d(region, region3d, v1)
dir = p1 - p0
if dir.length < 1:
continue
text = "RL " + self.format_value(verts[-1].z)
self.draw_label(text, p0, dir, gap=8, center=False)
class SectionDecorator(LevelDecorator):
basename = "IfcAnnotation/Section Level"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CALLOUT_GAP 8.0
#define CALLOUT_SIZE 64.0
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
in uint type[];
out float dist; // distance from starging point along segment
void callout_head(in vec4 dir, out vec4 head[4]) {
vec2 gap = dir.xy * CALLOUT_GAP;
vec2 tail = dir.xy * -CALLOUT_SIZE;
vec2 side = cross(vec3(dir.xy, 0), vec3(0, 0, 1)).xy * CALLOUT_GAP;
head[0] = vec4(tail + side, 0, 0);
head[1] = vec4(gap * 2 + side, 0, 0);
head[2] = vec4(gap, 0, 0);
head[3] = vec4(side, 0, 0);
}
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
uint t0 = type[0], t1 = type[1];
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
vec4 edge = p1w - p0w, dir = normalize(edge);
vec4 gap = dir * 16.0;
vec4 p;
// start edge callout
if (t0 == 1u) {
vec4 head[4];
callout_head(dir, head);
for(int i=0; i<4; i++) {
p = p0w + head[i];
gl_Position = WIN2CLIP(p);
EmitVertex();
}
EndPrimitive();
}
// stem
dist = 0;
gl_Position = p0;
EmitVertex();
dist = length(p1w - p0w);
gl_Position = p1;
EmitVertex();
EndPrimitive();
}
"""
def draw_labels(self, obj, splines):
region = self.context.region
region3d = self.context.region_data
for verts in splines:
v0 = verts[0]
v1 = verts[1]
p0 = location_3d_to_region_2d(region, region3d, v0)
p1 = location_3d_to_region_2d(region, region3d, v1)
dir = p1 - p0
if dir.length < 1:
continue
text = "RL " + self.format_value(verts[-1].z)
self.draw_label(text, p0 + dir.normalized() * 16, -dir, gap=16, center=False)
class BreakDecorator(BaseDecorator):
"""Decorator for dimension objects
- first edge of a mesh with zigzag thingy in the middle
Uses first two vertices in verts list.
"""
basename = "IfcAnnotation/Break"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define BREAK_LENGTH 32.0
#define BREAK_WIDTH 16.0
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1), pmw = (p0w + p1w) * .5;
vec4 edge = p1w - p0w, dir = normalize(edge);
vec4 gap = dir * 16.0;
vec4 p;
vec4 quart = dir * BREAK_LENGTH * .25;
vec4 side = vec4(cross(vec3(dir.xy, 0), vec3(0, 0, 1)).xy, 0, 0) * BREAK_WIDTH;
// TODO: check if there's enough length for zigzag
gl_Position = p0;
EmitVertex();
p = pmw;
gl_Position = WIN2CLIP(p);
EmitVertex();
p = pmw + quart - side;
gl_Position = WIN2CLIP(p);
EmitVertex();
p = pmw + quart * 3 + side;
gl_Position = WIN2CLIP(p);
EmitVertex();
p = pmw + quart * 4;
gl_Position = WIN2CLIP(p);
EmitVertex();
gl_Position = p1;
EmitVertex();
EndPrimitive();
}
"""
def decorate(self, obj):
if obj.data.is_editmode:
verts = self.get_editmesh_geom(obj)
else:
verts = self.get_mesh_geom(obj)
self.draw_lines(obj, verts, [(0, 1)])
def get_mesh_geom(self, obj):
# first vertices only
vertices = [obj.matrix_world @ obj.data.vertices[i].co for i in (0, 1)]
return vertices
def get_editmesh_geom(self, obj):
# first vertices only
mesh = bmesh.from_edit_mesh(obj.data)
vertices = [obj.matrix_world @ v.co for v in mesh.edges[0].verts]
return vertices
class GridDecorator(BaseDecorator):
basename = "IfcGridAxis/"
installed = None
DEF_GLSL = BaseDecorator.DEF_GLSL + """
#define CIRCLE_SIZE 16.0
#define DASH_SIZE 48.0
#define DASH_PATTERN 0x03C0FFFFU
"""
GEOM_GLSL = """
uniform vec2 winsize;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
out float dist; // distance from starging point along segment
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
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 * CIRCLE_SIZE;
vec4 p;
vec4 head[CIRCLE_SEGS];
circle_head(CIRCLE_SIZE, head);
dist = 0;
// start edge circle
for(int i=0; i<CIRCLE_SEGS; i++) {
p = p0w + head[i];
gl_Position = WIN2CLIP(p);
EmitVertex();
}
p = p0w + head[0];
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
// end edge circle
for(int i=0; i<CIRCLE_SEGS; i++) {
p = p1w + head[i];
gl_Position = WIN2CLIP(p);
EmitVertex();
}
p = p1w + head[0];
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
// stem
dist = 0;
p = p0w + gap;
gl_Position = WIN2CLIP(p);
EmitVertex();
dist = length(edge);
p = p1w - gap;
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
}
"""
FRAG_GLSL = """
in vec2 gl_FragCoord;
in float dist;
out vec4 fragColor;
void main() {
uint bit = uint(fract(dist / DASH_SIZE) * 32);
if ((DASH_PATTERN & (1U<<bit)) == 0U) discard;
fragColor = COLOR;
}
"""
def get_mesh_geom(self, obj):
# first vertices only
vertices = [obj.matrix_world @ obj.data.vertices[i].co for i in (0, 1)]
return vertices
def get_editmesh_geom(self, obj):
# first vertices only
mesh = bmesh.from_edit_mesh(obj.data)
vertices = [obj.matrix_world @ v.co for v in mesh.edges[0].verts]
return vertices
def decorate(self, obj):
if obj.data.is_editmode:
verts = self.get_editmesh_geom(obj)
else:
verts = self.get_mesh_geom(obj)
self.draw_lines(obj, verts, [(0, 1)])
self.draw_labels(obj, verts)
def draw_labels(self, obj, vertices):
region = self.context.region
region3d = self.context.region_data
v0 = Vector(vertices[0])
v1 = Vector(vertices[1])
p0 = location_3d_to_region_2d(region, region3d, v0)
p1 = location_3d_to_region_2d(region, region3d, v1)
dir = Vector((1, 0))
text = obj.BIMObjectProperties.attributes['AxisTag'].string_value
self.draw_label(text, p0, dir, vcenter=True, gap=0)
self.draw_label(text, p1, dir, vcenter=True, gap=0)
all_decorators = [
DimensionDecorator,
EqualityDecorator,
GridDecorator,
HiddenDecorator,
LeaderDecorator,
MiscDecorator,
PlanDecorator,
SectionDecorator,
StairDecorator,
BreakDecorator
]