当前位置: 代码迷 >> python >> 2D构造几何Python
  详细解决方案

2D构造几何Python

热度:64   发布时间:2023-06-13 15:16:24.0

我正在尝试建立一个简单的2D构造几何脚本,但是遇到了一些问题。 我在python中使用单元测试来测试一些代码,但是遇到错误,想知道我在做什么错。

1)假设类“ Union”确定一个点是在“左”节点还是“右”节点内(可以是任意的,因为我还有许多其他基于类QuadraticSurface的形状函数),但是为了简单起见我已经将Circle定义留在了代码中,因为它是在单元测试中实现的。 对于单元测试,它会创建两个节点L和R,如前所述,但是在这种情况下,它是两个以原点为中心的圆,一个半径为1,另一个半径为2。在这种情况下,它将读取三个点((0,0),(2,0),(1.5,0))。 我经历了许多迭代,都失败了,给我False不是True,反之亦然,特别是对于点(0,0)和(1.5,0)。 基本上,在单元测试中,它声明两个圆形成一个环,并且环内部或外部的任何内容均为True,而环本身中的任何内容均为False,也就是点(1.5,0)。 这是我本人从单元测试中收集的,可能是错误的,我在上面的代码中编写了测试文件。

2)如果该点在左节点和右节点中都位于,则其中包含的类“交集”应返回true。 我尝试使用逻辑“与”,但没有成功。

3)“ QuadraticSurface”没有为射线定义交点函数,对于如何找到任意形状的交点我有些迷惑。 但是,运行单元测试时,它会通过射线,并且在x = 3处有一个垂直平面,因此,当射线通过时,交叉点应该为(3,3)。

4) 有用的注释原始类表示终端节点(表面)。 运算符类表示两个节点的组合。 运算符的类不应实现其contains函数,但是将使用相交函数。

更新:我尝试了Prune建议的方法,但没有成功。 单元测试返回AssertionError:对于点(1.5,0),True不为false。 对于交点contains(p),对于点(1.5,0),False不成立。

基本代码

import numpy as np
import unittest

class Point(object) :

    def __init__(self, x, y) :
        self.x, self.y = x, y       
    def __add__(self,other):
        x = self.x + other.x
        y = self.y + other.y
        return Point(x,y)  
    def __mul__(self,scale):
        x = self.x * scale
        y = self.y * scale
        return Point(x,y) 
    def __str__(self) :
        return "Point(%.6F, %.6f) " % (self.x, self.y)

class Ray(object) :  
    def __init__(self, origin, direction) :
        self.origin = origin
        norm = np.sqrt(direction.x**2 + direction.y**2)
        self.direction = Point(direction.x/norm, direction.y/norm)
    def __str__(self) :
        return "Ray: r_0(%10.6f, %10.6f), d(%.6f %.6f) " % \
           (self.origin.x, self.origin.y, self.direction.x, self.direction.y)

class Node(object) :

    def contains(self, p) :
        raise NotImplementedError

    def intersections(self, r) :
        raise NotImplementedError

class Primitive(Node) :

    def __init__(self, surface, sense) :
        self.surface, self.sense = surface, sense

    def contains(self, p) :
        return (self.surface.f(p) < 0) == self.sense

    def intersections(self, r) :
        return self.surface.intersections(r)

class Operator(Node) :

    def __init__(self, L, R) :
        self.L, self.R = L, R

    def contains(self, p) :
        raise NotImplementedError

    def intersections(self, r) :
        # get intersections with left and right nodes
        pointsL = self.L.intersections(r)
        pointsR = self.R.intersections(r)
        # return the concatenated result
        return pointsL + pointsR

class Intersection(Operator):
    def __init__(self,L,R):
        super(Intersection,self).__init__(L,R)   
        self.L = L
        self.R = R

    def contains(self,p):
        return p >= self.L and p <= self.R

    def intersections(self):
        pass            
class Union(Operator):
    def __init__(self,L,R):    
        super(Union,self).__init__(L,R)
    def contains(self,p):
        return p <= self.L or p <= self.R

class Surface(object) :

    def f(self, p) :
        raise NotImplementedError

    def intersections(self, r) :
        raise NotImplementedError

class QuadraticSurface(Surface) :

    def __init__(self, A=0.0, B=0.0, C=0.0, D=0.0, E=0.0, F=0.0) :
        super(QuadraticSurface,self).__init__()
        self.A = A
        self.B = B
        self.C = C
        self.D = D
        self.E = E
        self.F = F

    def intersections(self, r) :
        self.r = r
        x = -self.F/self.D
        y = x

    def f(self, p) :
        x = p.x
        y = p.y
        return self.A*x**2 + self.B*y**2 + self.C*x*y + self.D*x + self.E*y + self.F

class Circle(QuadraticSurface):
    def __init__(self,r,a=0.0,b=0.0):
        super(Circle,self).__init__(A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0)
        self.r = r
        self.a = a
        self.b = b
        self.A = 1.0
        self.B = 1.0
        self.C = 0.0
        self.D = -2.0*self.a**2
        self.E = -2.0*self.b**2
        self.F = self.r

单元测试

class TestCSG(unittest.TestCase) :
    def setUp(self) :
        pass

    def get_circles(self) :
        # unit circle centered at origin
        c0 = Circle(1)
        # circle of radius two centered at the origin
        c1 = Circle(2)
        return c0, c1

    def testUnion_contains(self) :
        c0, c1 = self.get_circles()
        l, r = Primitive(c0, True), Primitive(c1, False)
        # everything outside c1 and inside c0
        u = Union(l, r)
        self.assertTrue(u.contains(Point(0, 0)))
        self.assertTrue(u.contains(Point(2, 0)))
        self.assertFalse(u.contains(Point(1.5, 0)))


    def testIntersection_contains(self) :

        c0, c1 = self.get_circles()
        l, r = Primitive(c0, False), Primitive(c1, True)
        # everything between c0 and c1
        i = Intersection(l, r)
        self.assertFalse(i.contains(Point(0, 0)))
        self.assertFalse(i.contains(Point(2, 0)))
        self.assertTrue(i.contains(Point(1.5, 0)))

    def testIntersection_intersections(self) :

        c0, c1 = self.get_circles()
        l, r = Primitive(c0, False), Primitive(c1, True)
        # everything between c0 and c1
        i = Intersection(l, r)
        # ray starting at (-3, 0) along x-axis
        ray = Ray(Point(-3, 0), Point(1, 0))

        # get intersections        
        ints = i.intersections(ray)
        # the order of the intersections depends on the implementation, but
        # the values should be unique.  hence, sort them according to 
        # x value.
        ints.sort(key = lambda p: p.x)
        reference_ints = [Point(i, 0) for i in (-2,-1, 1, 2)]        
        for i in range(4) :
            self.assertAlmostEqual(ints[i].x, reference_ints[i].x)

    def testQuadraticSurface_intersections(self) :

        # ray starting at origin and with a direction of 45 degrees
        ray = Ray(Point(0, 0), Point(1, 1))

        # vertical plane at x = 3
        v = QuadraticSurface(D=1, F=-3)
        ints = v.intersections(ray)
        self.assertEqual(len(ints), 1)
        self.assertAlmostEqual(ints[0].x, 3)
        self.assertAlmostEqual(ints[0].y, 3)

        # create a circle of radius 2 centered at (2, 2)      
        c = QuadraticSurface(A=1, B=1, D=-4, E=-4, F=4)
        ints = c.intersections(ray)
        ints.sort(key=lambda p: p.x)
        self.assertEqual(len(ints), 2)
        self.assertAlmostEqual(ints[0].x, (np.sqrt(8)-2)*np.cos(np.pi/4))
        self.assertAlmostEqual(ints[0].y, (np.sqrt(8)-2)*np.sin(np.pi/4))     
        self.assertAlmostEqual(ints[1].x, (np.sqrt(8)+2)*np.sin(np.pi/4))
        self.assertAlmostEqual(ints[1].y, (np.sqrt(8)+2)*np.sin(np.pi/4))

if __name__ == '__main__' :
    unittest.main()    

代码有几个问题。 即时的是,尽管我无法弄清您的类和对象体系结构,但是我可以看到包含contains的功能问题。 以联合为例,让我们看一下关键线。 我在前面添加了一条打印语句,以查看我们正在比较的内容(在跟踪程序时应该执行的操作)。

print p, self.L, self.R
if p <= self.L or self.R:

p是一点; self.L和self.R是没有有用表示形式的原语。

Point(1.500000, 0.000000)  <so.Primitive object at 0x2671c50> <so.Primitive object at 0x2671c90>

我看不到点和对象的<=定义,但似乎是成员身份。 但是,第二部分是不正确的:self.R不是布尔值。 您不能在“ and”和“ or”上分配任意运算。 我想你需要的是

if p <= self.L or p <= self.R:

实际上,请注意,您只需返回此值,即可完成整个功能

def contains(self,p):
    return p <= self.L or p <= self.R

我希望这可以解决您的问题。 顺便说一句,您的交集函数的问题是它什么都不返回。 我建议您使用“和”代替“或”来执行与上面的代码相同的操作。

最后, 在发布之前尝试自己调试问题:在每个问题方法的顶部添加打印语句以跟踪参数值,并在最后添加打印语句以报告返回值。 检查失败的表达式中的每个阶段。

最重要的是,确保在此处发布代码时正确记录了您的代码。 如果我对您的工作原理有所了解,那么我可能会在紧迫的问题之后发现更多问题。


我尝试了不同点的比较功能。 它们都给出相同的表达式结果:

p <= <circle_object> returns True
p >= <circle_object> returns False

现在,您已经更好地描述了代码,我可以看到问题所在:您尚未为该类定义“ <=”和“> =”运算符。 这意味着您只是在比较对象句柄。 结果并不能说明该点是在圆内还是圆外。

看看您如何在Point类中定义运算符? 您还需要做这样的事情来与Surface进行比较。