将3D多边形转换为2D,执行裁剪,然后转换回3D

时间:2016-08-17 18:02:48

标签: python 3d geometry

我的问题是我有两面墙,在3D空间中表示为2D平面(wallAwallB)。这些墙是重叠的。我需要将其转换为三个墙部分,一个用于wallA.intersect(wallB),一个用于wallA.diff(wallB),一个用于wallB.diff(wallA)

我认为我需要做的是将它们旋转到2D空间,而不改变它们的重叠,执行剪裁以识别差异和相交,然后将新墙旋转回原始平面。

墙壁不一定是垂直的,否则问题可能会更简单。

我的问题的剪辑部分可以使用pyclipper在2D中轻松解决。我遇到的问题是可以将墙壁可恢复地转换成2D的算法。

根据我的理解,与this question中的步骤类似但不完全相同。我看了transforms3D这看起来非常有用,但是我不太明白我需要使用哪些函数或什么组合来重现该算法。

这是我想要实现的一个例子,使用一对2 x 2垂直曲面的简单示例,在一个角落中重叠1 x 1正方形。

import pyclipper as pc

wallA= [(0,0,2), (2,0,2), (2,0,0), (0,0,0)]
wallB = [(1,0,3), (3,0,3), (3,0,1), (1,0,1)]
expected_overlaps = [[(1,0,2), (2,0,2), (2,0,1), (1,0,1)]]

wallA_2d = transform_to_2D(wallA, <whatever else is needed>)
wallB_2d = transform_to_2D(wallB, <whatever else is needed>)

scaledA = pc.scale_to_clipper(wallA_2d)
scaledB = pc.scale_to_clipper(wallB_2d)
clipper = pc.Pyclipper()
clipper.AddPath(scaledA, poly_type=pc.PT_SUBJECT, closed=True)
clipper.AddPath(scaledB, poly_type=pc.PT_CLIP, closed=True)

# just showing the intersection - differences are handled similarly
intersections = clipper.Execute(
    pc.CT_INTERSECTION, pc.PFT_NONZERO, pc.PFT_NONZERO)

intersections = [pc.scale_from_clipper(i) for i in intersections]
overlaps = [transform_to_3D(i, <whatever else is needed>) for i in intersections]

assert overlaps  == expected_overlaps

我正在寻找的是对transform_to_2dtransform_to_3d所需步骤的解释。

1 个答案:

答案 0 :(得分:2)

您可以简单地投射,而不是旋转。关键是将3d空间映射到2d平面,然后可以反转。 (当您回映时,投影产生的任何失真都将被撤消。)为此,您应首先找到包含两个墙的平面。以下是一些示例代码:

wallA = [(0,0,2), (2,0,2), (2,0,0), (0,0,0)]
wallB = [(1,0,3), (3,0,3), (3,0,1), (1,0,1)]
v = (0, 1, 0) # the normal vector
a = 0 # a number so that v[0] * x + v[1] * y + v[2] * z = a is the equation of the plane containing your walls

# To calculate the normal vector in general, 
# you would take the cross product of any two 
# vectors in the plane of your walls, e.g.
# (wallA[1] - wallA[0]) X (wallA[2] - wallA[0]).
# You can then solve for a.

proj_axis = max(range(3), key=lambda i: abs(v[i])) 
# this just needs to be any number such that v[proj_axis] != 0

def project(x):
    # Project onto either the xy, yz, or xz plane. (We choose the one that avoids degenerate configurations, which is the purpose of proj_axis.)
    # In this example, we would be projecting onto the xz plane.
    return tuple(c for i, c in enumerate(x) if i != proj_axis)

def project_inv(x):
    # Returns the vector w in the walls' plane such that project(w) equals x.
    w = list(x)
    w[proj_axis:proj_axis] = [0.0]
    c = a
    for i in range(3):
        c -= w[i] * v[i]
    c /= v[proj_axis]
    w[proj_axis] = c
    return tuple(w)

projA = [project(x) for x in wallA]
projB = [project(x) for x in wallB]
proj_intersection = intersection(projA, projB) # use your 2d algorithm here
intersection = [project_inv(x) for x in proj_intersection] # this is your intersection in 3d; you can do similar things for the other pieces