我试图设置一个简单的2D构造几何脚本,但遇到了一些问题。我在python中使用单元测试来测试我的一些代码,但遇到错误,并想知道我做错了什么。
1) '联盟' 类用于确定某个点是否位于' left'或者'对'节点(可以是任意的,因为我有许多基于QuadraticSurface类的其他形状函数),但是为了简单起见,我在代码中留下了Circle定义,因为它是在单元测试中实现的。对于单元测试的情况,它创建两个节点L和R,如前所述,但在这种情况下,它是两个以原点为中心的圆,一个半径为1,另一个半径为2。在这个特定情况下,它将读取三个点((0,0),(2,0),(1.5,0))。我经历了很多迭代,都失败了,给我False不是True或反之亦然,特别是对于点(0,0)和(1.5,0)。基本上,在单元测试中,它断言两个圆形成一个环,并且环的内部或外部的任何东西都是真的,环中的任何东西本身都是假的,即点(1.5,0)。这是我自己从单元测试中收集到的,我可能是错的,我编写了上面的部分,测试文件是给我的。
2)如果该点位于左侧和右侧节点中,则类' Intersection' where contains应返回true。我尝试过使用逻辑'和#39;但没有成功。
3)班级' QuadraticSurface' 没有为光线定义的交叉函数,而且我很少失去了如何找到一些任意形状的交叉点。然而,当单元测试运行时,它会传递一条光线,并在x = 3处传递一个垂直平面,所以对于它经过的光线,交点应该最终为(3,3)。
4) 有用的注释:原始类表示终端节点(表面)。 运算符类表示两个节点的组合。 运算符的类不应实现其contains函数,但是,interesctions函数将被使用。
更新:我尝试了Prune建议的方法,但没有成功。单元测试返回AssertionError:对于点(1.5,0),True不为false。并且对于交点包含(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()
答案 0 :(得分:1)
代码有几个问题。直接的一点是,虽然我无法弄清楚你的类和对象架构,但我可以看到包含的功能问题。以Union为例,让我们看一下关键线。我在前面添加了一个打印声明,看看我们在比较什么(你应该在跟踪你的程序时做过)。
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>
我没有看到&lt; =对于点和对象的定义,但它似乎是成员资格。但是,第二部分是不正确的:self.R不是布尔值。您不能在“和”和“或”上分发任意操作。我认为你需要的是
if p <= self.L or p <= self.R:
事实上,请注意,您只需返回此值,即可创建整个函数
def contains(self,p):
return p <= self.L or p <= self.R
我希望这可以解决你的问题。顺便说一下,你的交叉功能的问题是它不返回任何东西。我建议你和上面的代码一样,用“和”代替“或”。
最后,请尝试在发布之前自己调试问题:添加print语句以跟踪每个问题方法顶部的参数值,并打印语句以报告最后的返回值。检查失败表达式中的每个阶段。
最重要的是,请确保在此处发布时正确记录了您的代码。如果我知道你的东西是如何运作的话,我可能会发现更多的问题。
我尝试了各种各样的比较功能。所有这些都给出了相同的表达结果:
p <= <circle_object> returns True
p >= <circle_object> returns False
现在您已经更好地描述了代码,我可以看到问题:您没有为类定义“&lt; =”和“&gt; =”运算符。这意味着您只是比较对象句柄;结果没有说明该点是在圆圈的内部还是外部。
看看你如何在Point类中定义运算符?你需要做这样的事情来与Surface进行比较。