Пересечения трехмерных полигонов в питоне

Существуют ли какие-либо инструменты или библиотеки с открытым исходным кодом (в идеале на Python), которые доступны для выполнения большого количества пересечений с трехмерной геометрией, считанной из шейп-файла ESRI? Большинство тестов будут состоять из простых сегментов линий и полигонов.

Я изучил OGR 1.7.1 / GEOS 3.2.0, и хотя он загружает данные правильно, результирующие пересечения неверны, и большинство других доступных инструментов, похоже, основаны на этой работе.

Хотя CGAL был бы альтернативой, его лицензия не подходит. Библиотека универсальной геометрии Boost выглядит фантастически, но API огромен и, похоже, не поддерживает программы чтения wkt или wkb из коробки.


person Andrew Walker    schedule 31.03.2010    source источник


Ответы (2)


Обновление спустя почти 10 лет!

Вот код, который я написал 10 лет назад для первая версия моего проекта оптической трассировки лучей. В новой версии кода больше не используется полигон; теперь мы делаем все с сетками.

Код можно подчистить, много защитного копирования. Я не даю никаких гарантий его точности или полезности. Я попытался почти дословно вытащить его из приведенной выше ссылки GitHub в изолированный скрипт.

import numpy as np

def cmp_floats(a,b, atol=1e-12):
    return abs(a-b) < atol


def magnitude(vector):
   return np.sqrt(np.dot(np.array(vector), np.array(vector)))


def norm(vector):
   return np.array(vector)/magnitude(np.array(vector))


class Ray(object):
    """A ray in the global cartesian frame."""
    def __init__(self, position, direction):
        self.position = np.array(position)
        self.direction = norm(direction) 


class Polygon(object):
    """ Polygon constructed from greater than two points.
    
        Only convex polygons are allowed! 
    
        Order of points is of course important!
    """
    
    def __init__(self, points):
        super(Polygon, self).__init__()
        self.pts = points
        #check if points are in one plane
        assert len(self.pts) >= 3, "You need at least 3 points to build a Polygon"
        if len(self.pts) > 3:
            x_0 = np.array(self.pts[0])
            for i in range(1,len(self.pts)-2):
                #the determinant of the vectors (volume) must always be 0
                x_i = np.array(self.pts[i])
                x_i1 = np.array(self.pts[i+1])
                x_i2 = np.array(self.pts[i+2])
                det = np.linalg.det([x_0-x_i, x_0-x_i1, x_0-x_i2])
                assert cmp_floats( det, 0.0 ), "Points must be in a plane to create a Polygon"
                

    def on_surface(self, point):
        """Returns True if the point is on the polygon's surface and false otherwise."""
        n = len(self.pts)
        anglesum = 0
        p = np.array(point)

        for i in range(n):
            v1 = np.array(self.pts[i]) - p
            v2 = np.array(self.pts[(i+1)%n]) - p

            m1 = magnitude(v1)
            m2 = magnitude(v2)

            if cmp_floats( m1*m2 , 0. ):
                return True #point is one of the nodes
            else:
                # angle(normal, vector)
                costheta = np.dot(v1,v2)/(m1*m2)
            anglesum = anglesum + np.arccos(costheta)
        return cmp_floats( anglesum , 2*np.pi )


    def contains(self, point):
        return False


    def surface_identifier(self, surface_point, assert_on_surface = True):
        return "polygon"


    def surface_normal(self, ray, acute=False):
        vec1 = np.array(self.pts[0])-np.array(self.pts[1])
        vec2 = np.array(self.pts[0])-np.array(self.pts[2])
        normal = norm( np.cross(vec1,vec2) )
        return normal


    def intersection(self, ray):
        """Returns a intersection point with a ray and the polygon."""
        n = self.surface_normal(ray)

        #Ray is parallel to the polygon
        if cmp_floats( np.dot( np.array(ray.direction), n ), 0. ):
            return None
 
        t = 1/(np.dot(np.array(ray.direction),n)) * ( np.dot(n,np.array(self.pts[0])) - np.dot(n,np.array(ray.position)) )
        
        #Intersection point is behind the ray
        if t < 0.0:
            return None

        #Calculate intersection point
        point = np.array(ray.position) + t*np.array(ray.direction)
        
        #Check if intersection point is really in the polygon or only on the (infinite) plane
        if self.on_surface(point):
            return [list(point)]

        return None


if __name__ == '__main__':
    points = [[0,0,0],[0,0.1,0],[0.1,0.1,-0.03],[0.1,0,-0.03]]
    polygon = Polygon(points)
    ray = Ray(position=(0,0,0), direction=(0,0,1))
    print(polygon.intersection(ray))
person Daniel Farrell    schedule 10.01.2011
comment
+1, это то, что я в конечном итоге сделал, но, как вы указали в README pvtrace (кстати, классный проект), производительность для большого количества векторных операций в python может быть довольно низкой. Конечным результатом стала оболочка модуля расширения python вокруг кода Кристера Эриксона из книги «Обнаружение столкновений в реальном времени». Единственная причина, по которой я не помечаю это как ответ, заключается в том, что я жду, когда кто-нибудь включит лучшие 3D-тесты в OGR / GEOS. - person Andrew Walker; 11.01.2011
comment
Это похоже на фантастическую книгу, спасибо (где я могу найти код, который вы упомянули?). - person Daniel Farrell; 12.01.2011
comment
Кажется, во всей кодовой базе и документации нет ни одного класса с именем Polygon. Было бы полезно, если бы вы упомянули свой импорт в примерах, особенно при упоминании новой библиотеки, о которой никто не должен знать. - person Cerin; 23.05.2020
comment
@Cerin этот ответ относится к версии программного обеспечения 9-летней давности, вы ищете это github.com/danieljfarrell/pvtrace/blob/. - person Daniel Farrell; 23.05.2020
comment
@DanielFarrell Это не текущий опубликованный код. pip install pvtrace устанавливает что-то совершенно другое. Вам следует подумать о повторном добавлении функции пересечения лучей. - person Cerin; 23.05.2020
comment
Приветствуются запросы на вытягивание @Cerin! Рассмотрю для будущего выпуска. Спасибо. - person Daniel Farrell; 23.05.2020
comment
@DanielFarrell может pvtrace получить точки пересечения, как вы упомянули выше? - person Bamberghh; 24.10.2020
comment
Современная pvtrace больше не использует полигоны. Не стесняйтесь брать код из моего комментария выше и делать с ним, что хотите, как с изолированным скриптом Python. - person Daniel Farrell; 24.10.2020

Вы можете попробовать мою последнюю библиотеку Geometry3D на

pip install Geometry3D

Пример показан ниже

>>> from Geometry3D import *
>>>
>>> a = Point(1,1,1)
>>> b = Point(-1,1,1)
>>> c = Point(-1,-1,1)
>>> d = Point(1,-1,1)
>>> e = Point(1,1,-1)
>>> f = Point(-1,1,-1)
>>> g = Point(-1,-1,-1)
>>> h = Point(1,-1,-1)
>>> cph0 = Parallelepiped(Point(-1,-1,-1),Vector(2,0,0),Vector(0,2,0),Vector(0,0,2))
>>> cpg12 = ConvexPolygon((e,c,h))
>>> cpg13 = ConvexPolygon((e,f,c))
>>> cpg14 = ConvexPolygon((c,f,g))
>>> cpg15 = ConvexPolygon((h,c,g))
>>> cpg16 = ConvexPolygon((h,g,f,e))
>>> cph1 = ConvexPolyhedron((cpg12,cpg13,cpg14,cpg15,cpg16))
>>> a1 = Point(1.5,1.5,1.5)
>>> b1 = Point(-0.5,1.5,1.5)
>>> c1 = Point(-0.5,-0.5,1.5)
>>> d1 = Point(1.5,-0.5,1.5)
>>> e1 = Point(1.5,1.5,-0.5)
>>> f1 = Point(-0.2,1.5,-0.5)
>>> g1 = Point(-0.2,-0.5,-0.5)
>>> h1 = Point(1.5,-0.5,-0.5)
>>>
>>> cpg6 = ConvexPolygon((a1,d1,h1,e1))
>>> cpg7 = ConvexPolygon((a1,e1,f1,b1))
>>> cpg8 = ConvexPolygon((c1,b1,f1,g1))
>>> cpg9 = ConvexPolygon((c1,g1,h1,d1))
>>> cpg10 = ConvexPolygon((a1,b1,c1,d1))
>>> cpg11 = ConvexPolygon((e1,h1,g1,f1))
>>> cph2 = ConvexPolyhedron((cpg6,cpg7,cpg8,cpg9,cpg10,cpg11))
>>> cph3 = intersection(cph0,cph2)
>>>
>>> cph4 = intersection(cph1,cph2)
>>> r = Renderer()
>>> r.add((cph0,'r',1),normal_length = 0)
>>> r.add((cph1,'r',1),normal_length = 0)
>>> r.add((cph2,'g',1),normal_length = 0)
>>> r.add((cph3,'b',3),normal_length = 0.5)
>>> r.add((cph4,'y',3),normal_length = 0.5)
>>> r.show()

Пример изображения

Вы также можете ознакомиться с документацией Geomrtry3D.

person gmh2015    schedule 01.08.2020