drawing grid clipping

This commit is contained in:
QwiglyDee
2021-01-04 11:31:30 +07:00
parent 1fe2bd8332
commit cbcd04c81d
2 changed files with 131 additions and 8 deletions
@@ -1,4 +1,6 @@
import math
from mathutils import geometry
from mathutils import Vector
import bpy
@@ -285,3 +287,94 @@ def format_distance(value, isArea=False, hide_units=True):
tx_dist = fmt % value
return tx_dist
def parse_diagram_scale(camera):
"""Returns numeric value of scale"""
if camera.BIMCameraProperties.diagram_scale == "CUSTOM":
_, fraction = camera.BIMCameraProperties.custom_diagram_scale.split("|")
else:
_, fraction = camera.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
return float(numerator) / float(denominator)
def ortho_view_frame(camera, margin=0.015):
"""Calculates 2d bounding box of camera view area.
Similar to `bpy.types.Camera.view_frame`
:arg camera: camera of drawing
:type camera: bpy.types.Camera + BIMCameraProperties
:arg margin: margins, in scene units
:type margin: float
:return: (xmin, xmax, ymin, ymax) in local camera coordinates
"""
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
size = camera.ortho_scale
hwidth = size * .5
hheight = size * .5 * aspect
scale = parse_diagram_scale(camera)
xmarg = margin * scale
ymarg = margin * scale * aspect
return (-hwidth + xmarg, hwidth - xmarg, -hheight + ymarg, hheight - ymarg)
def clip_segment(bounds, segm):
"""Clipping line segment to bounds
:arg bounds: (xmin, xmax, ymin, ymax)
:arg segm: 2 vertices of the segment
:return: 2 new vertices of segment or None if segment outside the bounding box
"""
# LiangBarsky algorithm
def iszero(v):
return abs(v) < 1e-10
xmin, xmax, ymin, ymax = bounds
p1, p2 = segm
dlt = p2 - p1
q_l = p1.x - xmin
q_r = xmax - p1.x
q_t = p1.y - ymin
q_b = ymax - p1.y
pos = [1]
neg = [0]
if (iszero(dlt.x) and (q_l < 0 or q_r < 0)) or (iszero(dlt.y) and (q_t < 0 or q_b < 0)):
# parallel to a boundary and outside it
return None
if not iszero(dlt.x):
t_l = q_l / -dlt.x
t_r = q_r / dlt.x
if t_l < 0:
neg.append(t_l)
pos.append(t_r)
else:
neg.append(t_r)
pos.append(t_l)
if not iszero(dlt.y):
t_t = q_t / -dlt.y
t_b = q_b / dlt.y
if t_t < 0:
neg.append(t_t)
pos.append(t_b)
else:
neg.append(t_b)
pos.append(t_t)
t1 = max(neg)
t2 = min(pos)
if t1 > t2:
return None
p1c = p1 + dlt * t1
p2c = p1 + dlt * t2
return p1c, p2c
@@ -28,6 +28,7 @@ from . import helper
from bpy_extras.io_utils import ImportHelper
from itertools import cycle
from mathutils import Vector, Matrix, Euler, geometry
import bmesh
from math import radians, degrees, atan, tan, cos, sin
from pathlib import Path
from bpy.app.handlers import persistent
@@ -4226,13 +4227,42 @@ class CopyGrid(bpy.types.Operator):
collection.objects.unlink(obj)
source = [obj
for coll in bpy.data.collections
if coll.name.startswith('IfcGrid')
for obj in coll.all_objects
if obj.type == 'MESH']
for obj in source:
dstobj = obj.copy()
dstobj.data = dstobj.data.copy()
collection.objects.link(dstobj)
for coll in bpy.data.collections
if coll.name.startswith("IfcGrid")
for obj in coll.all_objects
if obj.name.startswith("IfcGridAxis")]
camera = [obj
for obj in collection.all_objects
if obj.type == 'CAMERA'][0]
clipping = camera.data.type == 'ORTHO'
bounds = helper.ortho_view_frame(camera.data) if clipping else None
for src in source:
bm = bmesh.new()
bm.from_mesh(src.data)
bm.verts.ensure_lookup_table()
if clipping:
proj = src.matrix_world @ camera.matrix_world.inverted()
unproj = src.matrix_world.inverted() @ camera.matrix_world
bm.transform(proj)
points_orig = [v.co for v in bm.verts[0:2]]
points_clip = helper.clip_segment(bounds, points_orig)
if points_clip is None:
continue
bm.verts[0].co = points_clip[0]
bm.verts[1].co = points_clip[1]
bm.transform(unproj)
dst = src.copy()
dst.data = bpy.data.meshes.new(dst.name)
bm.to_mesh(dst.data)
collection.objects.link(dst)
return {"FINISHED"}