test_polygon.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. from sympy.core.numbers import (Float, Rational, oo, pi)
  2. from sympy.core.singleton import S
  3. from sympy.core.symbol import (Symbol, symbols)
  4. from sympy.functions.elementary.complexes import Abs
  5. from sympy.functions.elementary.miscellaneous import sqrt
  6. from sympy.functions.elementary.trigonometric import (acos, cos, sin)
  7. from sympy.functions.elementary.trigonometric import tan
  8. from sympy.geometry import (Circle, Ellipse, GeometryError, Point, Point2D,
  9. Polygon, Ray, RegularPolygon, Segment, Triangle,
  10. are_similar, convex_hull, intersection, Line, Ray2D)
  11. from sympy.testing.pytest import raises, slow, warns
  12. from sympy.core.random import verify_numerically
  13. from sympy.geometry.polygon import rad, deg
  14. from sympy.integrals.integrals import integrate
  15. def feq(a, b):
  16. """Test if two floating point values are 'equal'."""
  17. t_float = Float("1.0E-10")
  18. return -t_float < a - b < t_float
  19. @slow
  20. def test_polygon():
  21. x = Symbol('x', real=True)
  22. y = Symbol('y', real=True)
  23. q = Symbol('q', real=True)
  24. u = Symbol('u', real=True)
  25. v = Symbol('v', real=True)
  26. w = Symbol('w', real=True)
  27. x1 = Symbol('x1', real=True)
  28. half = S.Half
  29. a, b, c = Point(0, 0), Point(2, 0), Point(3, 3)
  30. t = Triangle(a, b, c)
  31. assert Polygon(Point(0, 0)) == Point(0, 0)
  32. assert Polygon(a, Point(1, 0), b, c) == t
  33. assert Polygon(Point(1, 0), b, c, a) == t
  34. assert Polygon(b, c, a, Point(1, 0)) == t
  35. # 2 "remove folded" tests
  36. assert Polygon(a, Point(3, 0), b, c) == t
  37. assert Polygon(a, b, Point(3, -1), b, c) == t
  38. # remove multiple collinear points
  39. assert Polygon(Point(-4, 15), Point(-11, 15), Point(-15, 15),
  40. Point(-15, 33/5), Point(-15, -87/10), Point(-15, -15),
  41. Point(-42/5, -15), Point(-2, -15), Point(7, -15), Point(15, -15),
  42. Point(15, -3), Point(15, 10), Point(15, 15)) == \
  43. Polygon(Point(-15, -15), Point(15, -15), Point(15, 15), Point(-15, 15))
  44. p1 = Polygon(
  45. Point(0, 0), Point(3, -1),
  46. Point(6, 0), Point(4, 5),
  47. Point(2, 3), Point(0, 3))
  48. p2 = Polygon(
  49. Point(6, 0), Point(3, -1),
  50. Point(0, 0), Point(0, 3),
  51. Point(2, 3), Point(4, 5))
  52. p3 = Polygon(
  53. Point(0, 0), Point(3, 0),
  54. Point(5, 2), Point(4, 4))
  55. p4 = Polygon(
  56. Point(0, 0), Point(4, 4),
  57. Point(5, 2), Point(3, 0))
  58. p5 = Polygon(
  59. Point(0, 0), Point(4, 4),
  60. Point(0, 4))
  61. p6 = Polygon(
  62. Point(-11, 1), Point(-9, 6.6),
  63. Point(-4, -3), Point(-8.4, -8.7))
  64. p7 = Polygon(
  65. Point(x, y), Point(q, u),
  66. Point(v, w))
  67. p8 = Polygon(
  68. Point(x, y), Point(v, w),
  69. Point(q, u))
  70. p9 = Polygon(
  71. Point(0, 0), Point(4, 4),
  72. Point(3, 0), Point(5, 2))
  73. p10 = Polygon(
  74. Point(0, 2), Point(2, 2),
  75. Point(0, 0), Point(2, 0))
  76. p11 = Polygon(Point(0, 0), 1, n=3)
  77. p12 = Polygon(Point(0, 0), 1, 0, n=3)
  78. r = Ray(Point(-9, 6.6), Point(-9, 5.5))
  79. #
  80. # General polygon
  81. #
  82. assert p1 == p2
  83. assert len(p1.args) == 6
  84. assert len(p1.sides) == 6
  85. assert p1.perimeter == 5 + 2*sqrt(10) + sqrt(29) + sqrt(8)
  86. assert p1.area == 22
  87. assert not p1.is_convex()
  88. assert Polygon((-1, 1), (2, -1), (2, 1), (-1, -1), (3, 0)
  89. ).is_convex() is False
  90. # ensure convex for both CW and CCW point specification
  91. assert p3.is_convex()
  92. assert p4.is_convex()
  93. dict5 = p5.angles
  94. assert dict5[Point(0, 0)] == pi / 4
  95. assert dict5[Point(0, 4)] == pi / 2
  96. assert p5.encloses_point(Point(x, y)) is None
  97. assert p5.encloses_point(Point(1, 3))
  98. assert p5.encloses_point(Point(0, 0)) is False
  99. assert p5.encloses_point(Point(4, 0)) is False
  100. assert p1.encloses(Circle(Point(2.5, 2.5), 5)) is False
  101. assert p1.encloses(Ellipse(Point(2.5, 2), 5, 6)) is False
  102. assert p5.plot_interval('x') == [x, 0, 1]
  103. assert p5.distance(
  104. Polygon(Point(10, 10), Point(14, 14), Point(10, 14))) == 6 * sqrt(2)
  105. assert p5.distance(
  106. Polygon(Point(1, 8), Point(5, 8), Point(8, 12), Point(1, 12))) == 4
  107. with warns(UserWarning, \
  108. match="Polygons may intersect producing erroneous output"):
  109. Polygon(Point(0, 0), Point(1, 0), Point(1, 1)).distance(
  110. Polygon(Point(0, 0), Point(0, 1), Point(1, 1)))
  111. assert hash(p5) == hash(Polygon(Point(0, 0), Point(4, 4), Point(0, 4)))
  112. assert hash(p1) == hash(p2)
  113. assert hash(p7) == hash(p8)
  114. assert hash(p3) != hash(p9)
  115. assert p5 == Polygon(Point(4, 4), Point(0, 4), Point(0, 0))
  116. assert Polygon(Point(4, 4), Point(0, 4), Point(0, 0)) in p5
  117. assert p5 != Point(0, 4)
  118. assert Point(0, 1) in p5
  119. assert p5.arbitrary_point('t').subs(Symbol('t', real=True), 0) == \
  120. Point(0, 0)
  121. raises(ValueError, lambda: Polygon(
  122. Point(x, 0), Point(0, y), Point(x, y)).arbitrary_point('x'))
  123. assert p6.intersection(r) == [Point(-9, Rational(-84, 13)), Point(-9, Rational(33, 5))]
  124. assert p10.area == 0
  125. assert p11 == RegularPolygon(Point(0, 0), 1, 3, 0)
  126. assert p11 == p12
  127. assert p11.vertices[0] == Point(1, 0)
  128. assert p11.args[0] == Point(0, 0)
  129. p11.spin(pi/2)
  130. assert p11.vertices[0] == Point(0, 1)
  131. #
  132. # Regular polygon
  133. #
  134. p1 = RegularPolygon(Point(0, 0), 10, 5)
  135. p2 = RegularPolygon(Point(0, 0), 5, 5)
  136. raises(GeometryError, lambda: RegularPolygon(Point(0, 0), Point(0,
  137. 1), Point(1, 1)))
  138. raises(GeometryError, lambda: RegularPolygon(Point(0, 0), 1, 2))
  139. raises(ValueError, lambda: RegularPolygon(Point(0, 0), 1, 2.5))
  140. assert p1 != p2
  141. assert p1.interior_angle == pi*Rational(3, 5)
  142. assert p1.exterior_angle == pi*Rational(2, 5)
  143. assert p2.apothem == 5*cos(pi/5)
  144. assert p2.circumcenter == p1.circumcenter == Point(0, 0)
  145. assert p1.circumradius == p1.radius == 10
  146. assert p2.circumcircle == Circle(Point(0, 0), 5)
  147. assert p2.incircle == Circle(Point(0, 0), p2.apothem)
  148. assert p2.inradius == p2.apothem == (5 * (1 + sqrt(5)) / 4)
  149. p2.spin(pi / 10)
  150. dict1 = p2.angles
  151. assert dict1[Point(0, 5)] == 3 * pi / 5
  152. assert p1.is_convex()
  153. assert p1.rotation == 0
  154. assert p1.encloses_point(Point(0, 0))
  155. assert p1.encloses_point(Point(11, 0)) is False
  156. assert p2.encloses_point(Point(0, 4.9))
  157. p1.spin(pi/3)
  158. assert p1.rotation == pi/3
  159. assert p1.vertices[0] == Point(5, 5*sqrt(3))
  160. for var in p1.args:
  161. if isinstance(var, Point):
  162. assert var == Point(0, 0)
  163. else:
  164. assert var in (5, 10, pi / 3)
  165. assert p1 != Point(0, 0)
  166. assert p1 != p5
  167. # while spin works in place (notice that rotation is 2pi/3 below)
  168. # rotate returns a new object
  169. p1_old = p1
  170. assert p1.rotate(pi/3) == RegularPolygon(Point(0, 0), 10, 5, pi*Rational(2, 3))
  171. assert p1 == p1_old
  172. assert p1.area == (-250*sqrt(5) + 1250)/(4*tan(pi/5))
  173. assert p1.length == 20*sqrt(-sqrt(5)/8 + Rational(5, 8))
  174. assert p1.scale(2, 2) == \
  175. RegularPolygon(p1.center, p1.radius*2, p1._n, p1.rotation)
  176. assert RegularPolygon((0, 0), 1, 4).scale(2, 3) == \
  177. Polygon(Point(2, 0), Point(0, 3), Point(-2, 0), Point(0, -3))
  178. assert repr(p1) == str(p1)
  179. #
  180. # Angles
  181. #
  182. angles = p4.angles
  183. assert feq(angles[Point(0, 0)].evalf(), Float("0.7853981633974483"))
  184. assert feq(angles[Point(4, 4)].evalf(), Float("1.2490457723982544"))
  185. assert feq(angles[Point(5, 2)].evalf(), Float("1.8925468811915388"))
  186. assert feq(angles[Point(3, 0)].evalf(), Float("2.3561944901923449"))
  187. angles = p3.angles
  188. assert feq(angles[Point(0, 0)].evalf(), Float("0.7853981633974483"))
  189. assert feq(angles[Point(4, 4)].evalf(), Float("1.2490457723982544"))
  190. assert feq(angles[Point(5, 2)].evalf(), Float("1.8925468811915388"))
  191. assert feq(angles[Point(3, 0)].evalf(), Float("2.3561944901923449"))
  192. #
  193. # Triangle
  194. #
  195. p1 = Point(0, 0)
  196. p2 = Point(5, 0)
  197. p3 = Point(0, 5)
  198. t1 = Triangle(p1, p2, p3)
  199. t2 = Triangle(p1, p2, Point(Rational(5, 2), sqrt(Rational(75, 4))))
  200. t3 = Triangle(p1, Point(x1, 0), Point(0, x1))
  201. s1 = t1.sides
  202. assert Triangle(p1, p2, p1) == Polygon(p1, p2, p1) == Segment(p1, p2)
  203. raises(GeometryError, lambda: Triangle(Point(0, 0)))
  204. # Basic stuff
  205. assert Triangle(p1, p1, p1) == p1
  206. assert Triangle(p2, p2*2, p2*3) == Segment(p2, p2*3)
  207. assert t1.area == Rational(25, 2)
  208. assert t1.is_right()
  209. assert t2.is_right() is False
  210. assert t3.is_right()
  211. assert p1 in t1
  212. assert t1.sides[0] in t1
  213. assert Segment((0, 0), (1, 0)) in t1
  214. assert Point(5, 5) not in t2
  215. assert t1.is_convex()
  216. assert feq(t1.angles[p1].evalf(), pi.evalf()/2)
  217. assert t1.is_equilateral() is False
  218. assert t2.is_equilateral()
  219. assert t3.is_equilateral() is False
  220. assert are_similar(t1, t2) is False
  221. assert are_similar(t1, t3)
  222. assert are_similar(t2, t3) is False
  223. assert t1.is_similar(Point(0, 0)) is False
  224. assert t1.is_similar(t2) is False
  225. # Bisectors
  226. bisectors = t1.bisectors()
  227. assert bisectors[p1] == Segment(
  228. p1, Point(Rational(5, 2), Rational(5, 2)))
  229. assert t2.bisectors()[p2] == Segment(
  230. Point(5, 0), Point(Rational(5, 4), 5*sqrt(3)/4))
  231. p4 = Point(0, x1)
  232. assert t3.bisectors()[p4] == Segment(p4, Point(x1*(sqrt(2) - 1), 0))
  233. ic = (250 - 125*sqrt(2))/50
  234. assert t1.incenter == Point(ic, ic)
  235. # Inradius
  236. assert t1.inradius == t1.incircle.radius == 5 - 5*sqrt(2)/2
  237. assert t2.inradius == t2.incircle.radius == 5*sqrt(3)/6
  238. assert t3.inradius == t3.incircle.radius == x1**2/((2 + sqrt(2))*Abs(x1))
  239. # Exradius
  240. assert t1.exradii[t1.sides[2]] == 5*sqrt(2)/2
  241. # Excenters
  242. assert t1.excenters[t1.sides[2]] == Point2D(25*sqrt(2), -5*sqrt(2)/2)
  243. # Circumcircle
  244. assert t1.circumcircle.center == Point(2.5, 2.5)
  245. # Medians + Centroid
  246. m = t1.medians
  247. assert t1.centroid == Point(Rational(5, 3), Rational(5, 3))
  248. assert m[p1] == Segment(p1, Point(Rational(5, 2), Rational(5, 2)))
  249. assert t3.medians[p1] == Segment(p1, Point(x1/2, x1/2))
  250. assert intersection(m[p1], m[p2], m[p3]) == [t1.centroid]
  251. assert t1.medial == Triangle(Point(2.5, 0), Point(0, 2.5), Point(2.5, 2.5))
  252. # Nine-point circle
  253. assert t1.nine_point_circle == Circle(Point(2.5, 0),
  254. Point(0, 2.5), Point(2.5, 2.5))
  255. assert t1.nine_point_circle == Circle(Point(0, 0),
  256. Point(0, 2.5), Point(2.5, 2.5))
  257. # Perpendicular
  258. altitudes = t1.altitudes
  259. assert altitudes[p1] == Segment(p1, Point(Rational(5, 2), Rational(5, 2)))
  260. assert altitudes[p2].equals(s1[0])
  261. assert altitudes[p3] == s1[2]
  262. assert t1.orthocenter == p1
  263. t = S('''Triangle(
  264. Point(100080156402737/5000000000000, 79782624633431/500000000000),
  265. Point(39223884078253/2000000000000, 156345163124289/1000000000000),
  266. Point(31241359188437/1250000000000, 338338270939941/1000000000000000))''')
  267. assert t.orthocenter == S('''Point(-780660869050599840216997'''
  268. '''79471538701955848721853/80368430960602242240789074233100000000000000,'''
  269. '''20151573611150265741278060334545897615974257/16073686192120448448157'''
  270. '''8148466200000000000)''')
  271. # Ensure
  272. assert len(intersection(*bisectors.values())) == 1
  273. assert len(intersection(*altitudes.values())) == 1
  274. assert len(intersection(*m.values())) == 1
  275. # Distance
  276. p1 = Polygon(
  277. Point(0, 0), Point(1, 0),
  278. Point(1, 1), Point(0, 1))
  279. p2 = Polygon(
  280. Point(0, Rational(5)/4), Point(1, Rational(5)/4),
  281. Point(1, Rational(9)/4), Point(0, Rational(9)/4))
  282. p3 = Polygon(
  283. Point(1, 2), Point(2, 2),
  284. Point(2, 1))
  285. p4 = Polygon(
  286. Point(1, 1), Point(Rational(6)/5, 1),
  287. Point(1, Rational(6)/5))
  288. pt1 = Point(half, half)
  289. pt2 = Point(1, 1)
  290. '''Polygon to Point'''
  291. assert p1.distance(pt1) == half
  292. assert p1.distance(pt2) == 0
  293. assert p2.distance(pt1) == Rational(3)/4
  294. assert p3.distance(pt2) == sqrt(2)/2
  295. '''Polygon to Polygon'''
  296. # p1.distance(p2) emits a warning
  297. with warns(UserWarning, \
  298. match="Polygons may intersect producing erroneous output"):
  299. assert p1.distance(p2) == half/2
  300. assert p1.distance(p3) == sqrt(2)/2
  301. # p3.distance(p4) emits a warning
  302. with warns(UserWarning, \
  303. match="Polygons may intersect producing erroneous output"):
  304. assert p3.distance(p4) == (sqrt(2)/2 - sqrt(Rational(2)/25)/2)
  305. def test_convex_hull():
  306. p = [Point(-5, -1), Point(-2, 1), Point(-2, -1), Point(-1, -3), \
  307. Point(0, 0), Point(1, 1), Point(2, 2), Point(2, -1), Point(3, 1), \
  308. Point(4, -1), Point(6, 2)]
  309. ch = Polygon(p[0], p[3], p[9], p[10], p[6], p[1])
  310. #test handling of duplicate points
  311. p.append(p[3])
  312. #more than 3 collinear points
  313. another_p = [Point(-45, -85), Point(-45, 85), Point(-45, 26), \
  314. Point(-45, -24)]
  315. ch2 = Segment(another_p[0], another_p[1])
  316. assert convex_hull(*another_p) == ch2
  317. assert convex_hull(*p) == ch
  318. assert convex_hull(p[0]) == p[0]
  319. assert convex_hull(p[0], p[1]) == Segment(p[0], p[1])
  320. # no unique points
  321. assert convex_hull(*[p[-1]]*3) == p[-1]
  322. # collection of items
  323. assert convex_hull(*[Point(0, 0), \
  324. Segment(Point(1, 0), Point(1, 1)), \
  325. RegularPolygon(Point(2, 0), 2, 4)]) == \
  326. Polygon(Point(0, 0), Point(2, -2), Point(4, 0), Point(2, 2))
  327. def test_encloses():
  328. # square with a dimpled left side
  329. s = Polygon(Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1), \
  330. Point(S.Half, S.Half))
  331. # the following is True if the polygon isn't treated as closing on itself
  332. assert s.encloses(Point(0, S.Half)) is False
  333. assert s.encloses(Point(S.Half, S.Half)) is False # it's a vertex
  334. assert s.encloses(Point(Rational(3, 4), S.Half)) is True
  335. def test_triangle_kwargs():
  336. assert Triangle(sss=(3, 4, 5)) == \
  337. Triangle(Point(0, 0), Point(3, 0), Point(3, 4))
  338. assert Triangle(asa=(30, 2, 30)) == \
  339. Triangle(Point(0, 0), Point(2, 0), Point(1, sqrt(3)/3))
  340. assert Triangle(sas=(1, 45, 2)) == \
  341. Triangle(Point(0, 0), Point(2, 0), Point(sqrt(2)/2, sqrt(2)/2))
  342. assert Triangle(sss=(1, 2, 5)) is None
  343. assert deg(rad(180)) == 180
  344. def test_transform():
  345. pts = [Point(0, 0), Point(S.Half, Rational(1, 4)), Point(1, 1)]
  346. pts_out = [Point(-4, -10), Point(-3, Rational(-37, 4)), Point(-2, -7)]
  347. assert Triangle(*pts).scale(2, 3, (4, 5)) == Triangle(*pts_out)
  348. assert RegularPolygon((0, 0), 1, 4).scale(2, 3, (4, 5)) == \
  349. Polygon(Point(-2, -10), Point(-4, -7), Point(-6, -10), Point(-4, -13))
  350. # Checks for symmetric scaling
  351. assert RegularPolygon((0, 0), 1, 4).scale(2, 2) == \
  352. RegularPolygon(Point2D(0, 0), 2, 4, 0)
  353. def test_reflect():
  354. x = Symbol('x', real=True)
  355. y = Symbol('y', real=True)
  356. b = Symbol('b')
  357. m = Symbol('m')
  358. l = Line((0, b), slope=m)
  359. p = Point(x, y)
  360. r = p.reflect(l)
  361. dp = l.perpendicular_segment(p).length
  362. dr = l.perpendicular_segment(r).length
  363. assert verify_numerically(dp, dr)
  364. assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((3, 0), slope=oo)) \
  365. == Triangle(Point(5, 0), Point(4, 0), Point(4, 2))
  366. assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((0, 3), slope=oo)) \
  367. == Triangle(Point(-1, 0), Point(-2, 0), Point(-2, 2))
  368. assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((0, 3), slope=0)) \
  369. == Triangle(Point(1, 6), Point(2, 6), Point(2, 4))
  370. assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((3, 0), slope=0)) \
  371. == Triangle(Point(1, 0), Point(2, 0), Point(2, -2))
  372. def test_bisectors():
  373. p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
  374. p = Polygon(Point(0, 0), Point(2, 0), Point(1, 1), Point(0, 3))
  375. q = Polygon(Point(1, 0), Point(2, 0), Point(3, 3), Point(-1, 5))
  376. poly = Polygon(Point(3, 4), Point(0, 0), Point(8, 7), Point(-1, 1), Point(19, -19))
  377. t = Triangle(p1, p2, p3)
  378. assert t.bisectors()[p2] == Segment(Point(1, 0), Point(0, sqrt(2) - 1))
  379. assert p.bisectors()[Point2D(0, 3)] == Ray2D(Point2D(0, 3), \
  380. Point2D(sin(acos(2*sqrt(5)/5)/2), 3 - cos(acos(2*sqrt(5)/5)/2)))
  381. assert q.bisectors()[Point2D(-1, 5)] == \
  382. Ray2D(Point2D(-1, 5), Point2D(-1 + sqrt(29)*(5*sin(acos(9*sqrt(145)/145)/2) + \
  383. 2*cos(acos(9*sqrt(145)/145)/2))/29, sqrt(29)*(-5*cos(acos(9*sqrt(145)/145)/2) + \
  384. 2*sin(acos(9*sqrt(145)/145)/2))/29 + 5))
  385. assert poly.bisectors()[Point2D(-1, 1)] == Ray2D(Point2D(-1, 1), \
  386. Point2D(-1 + sin(acos(sqrt(26)/26)/2 + pi/4), 1 - sin(-acos(sqrt(26)/26)/2 + pi/4)))
  387. def test_incenter():
  388. assert Triangle(Point(0, 0), Point(1, 0), Point(0, 1)).incenter \
  389. == Point(1 - sqrt(2)/2, 1 - sqrt(2)/2)
  390. def test_inradius():
  391. assert Triangle(Point(0, 0), Point(4, 0), Point(0, 3)).inradius == 1
  392. def test_incircle():
  393. assert Triangle(Point(0, 0), Point(2, 0), Point(0, 2)).incircle \
  394. == Circle(Point(2 - sqrt(2), 2 - sqrt(2)), 2 - sqrt(2))
  395. def test_exradii():
  396. t = Triangle(Point(0, 0), Point(6, 0), Point(0, 2))
  397. assert t.exradii[t.sides[2]] == (-2 + sqrt(10))
  398. def test_medians():
  399. t = Triangle(Point(0, 0), Point(1, 0), Point(0, 1))
  400. assert t.medians[Point(0, 0)] == Segment(Point(0, 0), Point(S.Half, S.Half))
  401. def test_medial():
  402. assert Triangle(Point(0, 0), Point(1, 0), Point(0, 1)).medial \
  403. == Triangle(Point(S.Half, 0), Point(S.Half, S.Half), Point(0, S.Half))
  404. def test_nine_point_circle():
  405. assert Triangle(Point(0, 0), Point(1, 0), Point(0, 1)).nine_point_circle \
  406. == Circle(Point2D(Rational(1, 4), Rational(1, 4)), sqrt(2)/4)
  407. def test_eulerline():
  408. assert Triangle(Point(0, 0), Point(1, 0), Point(0, 1)).eulerline \
  409. == Line(Point2D(0, 0), Point2D(S.Half, S.Half))
  410. assert Triangle(Point(0, 0), Point(10, 0), Point(5, 5*sqrt(3))).eulerline \
  411. == Point2D(5, 5*sqrt(3)/3)
  412. assert Triangle(Point(4, -6), Point(4, -1), Point(-3, 3)).eulerline \
  413. == Line(Point2D(Rational(64, 7), 3), Point2D(Rational(-29, 14), Rational(-7, 2)))
  414. def test_intersection():
  415. poly1 = Triangle(Point(0, 0), Point(1, 0), Point(0, 1))
  416. poly2 = Polygon(Point(0, 1), Point(-5, 0),
  417. Point(0, -4), Point(0, Rational(1, 5)),
  418. Point(S.Half, -0.1), Point(1, 0), Point(0, 1))
  419. assert poly1.intersection(poly2) == [Point2D(Rational(1, 3), 0),
  420. Segment(Point(0, Rational(1, 5)), Point(0, 0)),
  421. Segment(Point(1, 0), Point(0, 1))]
  422. assert poly2.intersection(poly1) == [Point(Rational(1, 3), 0),
  423. Segment(Point(0, 0), Point(0, Rational(1, 5))),
  424. Segment(Point(1, 0), Point(0, 1))]
  425. assert poly1.intersection(Point(0, 0)) == [Point(0, 0)]
  426. assert poly1.intersection(Point(-12, -43)) == []
  427. assert poly2.intersection(Line((-12, 0), (12, 0))) == [Point(-5, 0),
  428. Point(0, 0), Point(Rational(1, 3), 0), Point(1, 0)]
  429. assert poly2.intersection(Line((-12, 12), (12, 12))) == []
  430. assert poly2.intersection(Ray((-3, 4), (1, 0))) == [Segment(Point(1, 0),
  431. Point(0, 1))]
  432. assert poly2.intersection(Circle((0, -1), 1)) == [Point(0, -2),
  433. Point(0, 0)]
  434. assert poly1.intersection(poly1) == [Segment(Point(0, 0), Point(1, 0)),
  435. Segment(Point(0, 1), Point(0, 0)), Segment(Point(1, 0), Point(0, 1))]
  436. assert poly2.intersection(poly2) == [Segment(Point(-5, 0), Point(0, -4)),
  437. Segment(Point(0, -4), Point(0, Rational(1, 5))),
  438. Segment(Point(0, Rational(1, 5)), Point(S.Half, Rational(-1, 10))),
  439. Segment(Point(0, 1), Point(-5, 0)),
  440. Segment(Point(S.Half, Rational(-1, 10)), Point(1, 0)),
  441. Segment(Point(1, 0), Point(0, 1))]
  442. assert poly2.intersection(Triangle(Point(0, 1), Point(1, 0), Point(-1, 1))) \
  443. == [Point(Rational(-5, 7), Rational(6, 7)), Segment(Point2D(0, 1), Point(1, 0))]
  444. assert poly1.intersection(RegularPolygon((-12, -15), 3, 3)) == []
  445. def test_parameter_value():
  446. t = Symbol('t')
  447. sq = Polygon((0, 0), (0, 1), (1, 1), (1, 0))
  448. assert sq.parameter_value((0.5, 1), t) == {t: Rational(3, 8)}
  449. q = Polygon((0, 0), (2, 1), (2, 4), (4, 0))
  450. assert q.parameter_value((4, 0), t) == {t: -6 + 3*sqrt(5)} # ~= 0.708
  451. raises(ValueError, lambda: sq.parameter_value((5, 6), t))
  452. raises(ValueError, lambda: sq.parameter_value(Circle(Point(0, 0), 1), t))
  453. def test_issue_12966():
  454. poly = Polygon(Point(0, 0), Point(0, 10), Point(5, 10), Point(5, 5),
  455. Point(10, 5), Point(10, 0))
  456. t = Symbol('t')
  457. pt = poly.arbitrary_point(t)
  458. DELTA = 5/poly.perimeter
  459. assert [pt.subs(t, DELTA*i) for i in range(int(1/DELTA))] == [
  460. Point(0, 0), Point(0, 5), Point(0, 10), Point(5, 10),
  461. Point(5, 5), Point(10, 5), Point(10, 0), Point(5, 0)]
  462. def test_second_moment_of_area():
  463. x, y = symbols('x, y')
  464. # triangle
  465. p1, p2, p3 = [(0, 0), (4, 0), (0, 2)]
  466. p = (0, 0)
  467. # equation of hypotenuse
  468. eq_y = (1-x/4)*2
  469. I_yy = integrate((x**2) * (integrate(1, (y, 0, eq_y))), (x, 0, 4))
  470. I_xx = integrate(1 * (integrate(y**2, (y, 0, eq_y))), (x, 0, 4))
  471. I_xy = integrate(x * (integrate(y, (y, 0, eq_y))), (x, 0, 4))
  472. triangle = Polygon(p1, p2, p3)
  473. assert (I_xx - triangle.second_moment_of_area(p)[0]) == 0
  474. assert (I_yy - triangle.second_moment_of_area(p)[1]) == 0
  475. assert (I_xy - triangle.second_moment_of_area(p)[2]) == 0
  476. # rectangle
  477. p1, p2, p3, p4=[(0, 0), (4, 0), (4, 2), (0, 2)]
  478. I_yy = integrate((x**2) * integrate(1, (y, 0, 2)), (x, 0, 4))
  479. I_xx = integrate(1 * integrate(y**2, (y, 0, 2)), (x, 0, 4))
  480. I_xy = integrate(x * integrate(y, (y, 0, 2)), (x, 0, 4))
  481. rectangle = Polygon(p1, p2, p3, p4)
  482. assert (I_xx - rectangle.second_moment_of_area(p)[0]) == 0
  483. assert (I_yy - rectangle.second_moment_of_area(p)[1]) == 0
  484. assert (I_xy - rectangle.second_moment_of_area(p)[2]) == 0
  485. r = RegularPolygon(Point(0, 0), 5, 3)
  486. assert r.second_moment_of_area() == (1875*sqrt(3)/S(32), 1875*sqrt(3)/S(32), 0)
  487. def test_first_moment():
  488. a, b = symbols('a, b', positive=True)
  489. # rectangle
  490. p1 = Polygon((0, 0), (a, 0), (a, b), (0, b))
  491. assert p1.first_moment_of_area() == (a*b**2/8, a**2*b/8)
  492. assert p1.first_moment_of_area((a/3, b/4)) == (-3*a*b**2/32, -a**2*b/9)
  493. p1 = Polygon((0, 0), (40, 0), (40, 30), (0, 30))
  494. assert p1.first_moment_of_area() == (4500, 6000)
  495. # triangle
  496. p2 = Polygon((0, 0), (a, 0), (a/2, b))
  497. assert p2.first_moment_of_area() == (4*a*b**2/81, a**2*b/24)
  498. assert p2.first_moment_of_area((a/8, b/6)) == (-25*a*b**2/648, -5*a**2*b/768)
  499. p2 = Polygon((0, 0), (12, 0), (12, 30))
  500. assert p2.first_moment_of_area() == (S(1600)/3, -S(640)/3)
  501. def test_section_modulus_and_polar_second_moment_of_area():
  502. a, b = symbols('a, b', positive=True)
  503. x, y = symbols('x, y')
  504. rectangle = Polygon((0, b), (0, 0), (a, 0), (a, b))
  505. assert rectangle.section_modulus(Point(x, y)) == (a*b**3/12/(-b/2 + y), a**3*b/12/(-a/2 + x))
  506. assert rectangle.polar_second_moment_of_area() == a**3*b/12 + a*b**3/12
  507. convex = RegularPolygon((0, 0), 1, 6)
  508. assert convex.section_modulus() == (Rational(5, 8), sqrt(3)*Rational(5, 16))
  509. assert convex.polar_second_moment_of_area() == 5*sqrt(3)/S(8)
  510. concave = Polygon((0, 0), (1, 8), (3, 4), (4, 6), (7, 1))
  511. assert concave.section_modulus() == (Rational(-6371, 429), Rational(-9778, 519))
  512. assert concave.polar_second_moment_of_area() == Rational(-38669, 252)
  513. def test_cut_section():
  514. # concave polygon
  515. p = Polygon((-1, -1), (1, Rational(5, 2)), (2, 1), (3, Rational(5, 2)), (4, 2), (5, 3), (-1, 3))
  516. l = Line((0, 0), (Rational(9, 2), 3))
  517. p1 = p.cut_section(l)[0]
  518. p2 = p.cut_section(l)[1]
  519. assert p1 == Polygon(
  520. Point2D(Rational(-9, 13), Rational(-6, 13)), Point2D(1, Rational(5, 2)), Point2D(Rational(24, 13), Rational(16, 13)),
  521. Point2D(Rational(12, 5), Rational(8, 5)), Point2D(3, Rational(5, 2)), Point2D(Rational(24, 7), Rational(16, 7)),
  522. Point2D(Rational(9, 2), 3), Point2D(-1, 3), Point2D(-1, Rational(-2, 3)))
  523. assert p2 == Polygon(Point2D(-1, -1), Point2D(Rational(-9, 13), Rational(-6, 13)), Point2D(Rational(24, 13), Rational(16, 13)),
  524. Point2D(2, 1), Point2D(Rational(12, 5), Rational(8, 5)), Point2D(Rational(24, 7), Rational(16, 7)), Point2D(4, 2), Point2D(5, 3),
  525. Point2D(Rational(9, 2), 3), Point2D(-1, Rational(-2, 3)))
  526. # convex polygon
  527. p = RegularPolygon(Point2D(0, 0), 6, 6)
  528. s = p.cut_section(Line((0, 0), slope=1))
  529. assert s[0] == Polygon(Point2D(-3*sqrt(3) + 9, -3*sqrt(3) + 9), Point2D(3, 3*sqrt(3)),
  530. Point2D(-3, 3*sqrt(3)), Point2D(-6, 0), Point2D(-9 + 3*sqrt(3), -9 + 3*sqrt(3)))
  531. assert s[1] == Polygon(Point2D(6, 0), Point2D(-3*sqrt(3) + 9, -3*sqrt(3) + 9),
  532. Point2D(-9 + 3*sqrt(3), -9 + 3*sqrt(3)), Point2D(-3, -3*sqrt(3)), Point2D(3, -3*sqrt(3)))
  533. # case where line does not intersects but coincides with the edge of polygon
  534. a, b = 20, 10
  535. t1, t2, t3, t4 = [(0, b), (0, 0), (a, 0), (a, b)]
  536. p = Polygon(t1, t2, t3, t4)
  537. p1, p2 = p.cut_section(Line((0, b), slope=0))
  538. assert p1 == None
  539. assert p2 == Polygon(Point2D(0, 10), Point2D(0, 0), Point2D(20, 0), Point2D(20, 10))
  540. p3, p4 = p.cut_section(Line((0, 0), slope=0))
  541. assert p3 == Polygon(Point2D(0, 10), Point2D(0, 0), Point2D(20, 0), Point2D(20, 10))
  542. assert p4 == None
  543. # case where the line does not intersect with a polygon at all
  544. raises(ValueError, lambda: p.cut_section(Line((0, a), slope=0)))
  545. def test_type_of_triangle():
  546. # Isoceles triangle
  547. p1 = Polygon(Point(0, 0), Point(5, 0), Point(2, 4))
  548. assert p1.is_isosceles() == True
  549. assert p1.is_scalene() == False
  550. assert p1.is_equilateral() == False
  551. # Scalene triangle
  552. p2 = Polygon (Point(0, 0), Point(0, 2), Point(4, 0))
  553. assert p2.is_isosceles() == False
  554. assert p2.is_scalene() == True
  555. assert p2.is_equilateral() == False
  556. # Equilateral triagle
  557. p3 = Polygon(Point(0, 0), Point(6, 0), Point(3, sqrt(27)))
  558. assert p3.is_isosceles() == True
  559. assert p3.is_scalene() == False
  560. assert p3.is_equilateral() == True
  561. def test_do_poly_distance():
  562. # Non-intersecting polygons
  563. square1 = Polygon (Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0))
  564. triangle1 = Polygon(Point(1, 2), Point(2, 2), Point(2, 1))
  565. assert square1._do_poly_distance(triangle1) == sqrt(2)/2
  566. # Polygons which sides intersect
  567. square2 = Polygon(Point(1, 0), Point(2, 0), Point(2, 1), Point(1, 1))
  568. with warns(UserWarning, \
  569. match="Polygons may intersect producing erroneous output", test_stacklevel=False):
  570. assert square1._do_poly_distance(square2) == 0
  571. # Polygons which bodies intersect
  572. triangle2 = Polygon(Point(0, -1), Point(2, -1), Point(S.Half, S.Half))
  573. with warns(UserWarning, \
  574. match="Polygons may intersect producing erroneous output", test_stacklevel=False):
  575. assert triangle2._do_poly_distance(square1) == 0