mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
drawing grid clipping
This commit is contained in:
@@ -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
|
||||
"""
|
||||
# Liang–Barsky 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"}
|
||||
|
||||
Reference in New Issue
Block a user