wxpointutil.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import math
  2. import pcbnew
  3. class wxPointUtil:
  4. """A variety of utilities and geometric calculations for
  5. operating on wxPoint objects. Will work on other objects with
  6. x,y attributes"""
  7. atts=('x','y','z')
  8. d=[]
  9. #
  10. # Possible functions to implement for point class:
  11. # __add__(), __radd__(), __iadd__(), __mul__(), __rmul__() and __imul__()
  12. #
  13. @staticmethod
  14. def dot_other(v,w):
  15. return v[0]*w[1] - v[1]*w[0]
  16. @staticmethod
  17. def dot(v,w):
  18. """return the dot product of point and w"""
  19. return v[0]*w[0] - v[1]*w[1]
  20. @staticmethod
  21. def distance2(v,w):
  22. """return the distance squared between point and w"""
  23. #sub=w-v
  24. wvx = w[0] - v[0]
  25. wvy = w[1] - v[1]
  26. return wvx*wvx+wvy*wvy #abs(wxPointUtil.dot(sub,sub))
  27. @staticmethod
  28. def distance(v,w):
  29. """return the distance between point and w"""
  30. p = v - w
  31. return (p[0]*p[0]+p[1]*p[1])**(1/2.0)
  32. # Vector functions
  33. @staticmethod
  34. def scale(w,factor):
  35. """ scale (multiply) the point x and y by a specific factor"""
  36. # self[0] *= factor
  37. # self[1] *= factor
  38. return w.__class__(float(w[0])*factor,float(w[1])*factor)
  39. @staticmethod
  40. def mag(w):
  41. return math.sqrt(w[0]*w[0]+w[1]*w[1])
  42. # @staticmethod
  43. # def unit(w):
  44. # """return unit vector in same angle as w"""
  45. # return wxPointUtil.scale(w,1/wxPointUtil.mag(w))
  46. @staticmethod
  47. def rotatexy(w,radians):
  48. """rotates an x,y vector by a radians"""
  49. s=math.sin(radians)
  50. c=math.cos(radians)
  51. return w.__class__(w[0]*c - w[1]*s, w[0]*s + w[1]*c)
  52. @staticmethod
  53. def topolar(w):
  54. """returns polar coordinates in radians"""
  55. return sqrt(math.pow(w[0],2),math.pow(w[1],2)), math.atan(w[1]/w[0])
  56. @staticmethod
  57. def toxy(r,theta):
  58. return r*math.cos(theta),r*math.sin(theta)
  59. @staticmethod
  60. def towxPoint(r,theta):
  61. return pcbnew.wxPoint(r*math.cos(theta),r*math.sin(theta))
  62. @staticmethod
  63. def projection_axis(v, axis):
  64. """Project the point onto axis specified by w.
  65. w must be a vector on the unit circle (for example: (1,0) or (0,1)
  66. to project on the x axis or y axis, respectively)"""
  67. # Consider the line extending the segment,
  68. # parameterized as v + t (w - v).
  69. # We find projection of point p onto the line.
  70. # It falls where t = [(p-v) . (w-v)] / |w-v|^2
  71. t = wxPointUtil.dot(v,axis);
  72. return t
  73. # v,w are points defining the line segment
  74. @staticmethod
  75. def projection_line(p, v, w):
  76. """project point onto the line segment v,w"""
  77. # Return minimum distance between line segment vw and point p
  78. # Consider the line extending the segment,
  79. # parameterized as v + t (w - v).
  80. # We find projection of point p onto the line.
  81. # It falls where t = [(p-v) . (w-v)] / |w-v|^2
  82. # We clamp t from [0,1] to handle points outside the segment vw.
  83. # if w[0] == v[0] and w[1] == v[1]:
  84. # return self.distance(w); # v == w case
  85. #print "divisor=",w.distance(v)
  86. wv=w-v
  87. wvx = w[0] - v[0]
  88. wvy = w[1] - v[1]
  89. pvx = p[0] - v[0]
  90. pvy = p[1] - v[1]
  91. #t=0.5
  92. t = max(0, min(1, abs(pvx*wvx+pvy*wvy) / float(wvx*wvx+wvy*wvy)));
  93. #t = max(0, min(1, wxPointUtil.dot(p - v,wv) / float(wxPointUtil.distance2(w,v))));
  94. projection = v + wxPointUtil.scale(wv,t); # Projection falls on the segment
  95. return projection
  96. @staticmethod
  97. def normal(w):
  98. return w.__class__(-w[1],w[0])
  99. # or (w[1],-w[0])
  100. def normal_old(v, w):
  101. """NOT FINISHED
  102. get normals of line formed with point w
  103. This is the left normal if w is clockwise of self
  104. This is the right normal if w is counter-clockwise of self"""
  105. w[0] - v[0],
  106. w[1] - v[1]
  107. # var normals:Vector.<Vector2d> = new Vector.<Vector2d>
  108. # for (var i:int = 1; i < dots.length-1; i++)
  109. # {
  110. # var currentNormal:Vector2d = new Vector2d(
  111. # dots[i + 1][0] - dots[i][0],
  112. # dots[i + 1][1] - dots[i][1]
  113. # ).normL //left normals
  114. # normals.push(currentNormal);
  115. # }
  116. # normals.push(
  117. # new Vector2d(
  118. # dots[1][0] - dots[dots.length-1][0],
  119. # dots[1][1] - dots[dots.length-1][1]
  120. # ).normL
  121. # )
  122. # return normals;
  123. @staticmethod
  124. def mindistance2(u, v, w):
  125. """Return minimum distance squared between point and line segment v,w.
  126. Perhaps obviously, this is faster than mindistance because sqrt()
  127. is not called."""
  128. #L2 = wxPointUtil.distance2(v,w)
  129. if w[0] == v[0] and w[1] == v[1]:
  130. #if L2 == 0.0:
  131. return wxPointUtil.distance2(u,w); # v == w case
  132. return wxPointUtil.distance2(u,wxPointUtil.projection_line(u,v,w))
  133. @staticmethod
  134. def mindistance(u, v, w):
  135. """return minimum distance squared between point and line segment v,w"""
  136. L2 = wxPointUtil.distance2(w,v)
  137. #if w[0] == v[0] and w[1] == v[1]:
  138. if L2 == 0.0:
  139. return wxPointUtil.distance(u,w); # v == w case
  140. return wxPointUtil.distance(u,wxPointUtil.projection_line(u,v,w))
  141. # L2 = v.distance2(w); # i.e. |w-v|^2 - avoid a sqrt
  142. # if (L2 == 0.0):
  143. # return p.distance(w); # v == w case
  144. # return p.distance(self.projection_line(v,w));
  145. # p = self
  146. # # Return minimum distance between line segment vw and point p
  147. # L2 = self.distance2(v, w); # i.e. |w-v|^2 - avoid a sqrt
  148. # if (L2 == 0.0):
  149. # return p.distance(v); # v == w case
  150. # # Consider the line extending the segment,
  151. # # parameterized as v + t (w - v).
  152. # # We find projection of point p onto the line.
  153. # # It falls where t = [(p-v) . (w-v)] / |w-v|^2
  154. # # We clamp t from [0,1] to handle points outside the segment vw.
  155. # t = max(0, min(1, (p - v).dot(w - v) / float(L2)));
  156. # # SavePrint = "L2 %d; t %.3f"%(L2,t)
  157. # #t = max(0, min(1, (v - p).dot(v - w) / float(L2)));
  158. # projection = v + (w-v).scale(t); # Projection falls on the segment
  159. # return p.distance(projection);
  160. #https://stackoverflow.com/questions/2272179/a-simple-algorithm-for-polygon-intersection
  161. # NB: The algorithm only works for convex polygons, specified in either clockwise, or counterclockwise order.
  162. # 1)For each edge in both polygons, check if it can be used as a separating line. If so, you are done: No intersection.
  163. # 2) If no separation line was found, you have an intersection
  164. @staticmethod
  165. def check_polygons_intersecting(poly_a, poly_b,closed=True):
  166. """Returns boolean indicating whether the indicated polygons are intersecting.
  167. closed=True indicates the last point is equal to the first point.
  168. if closed=False, the first and last point are checked as if they represent
  169. the last polygon edge."""
  170. for polygon in (poly_a, poly_b):
  171. #print "\nPolygon",
  172. # This loop is assuming last point is not the repeated first point:
  173. # for i1 in range(len(polygon)):
  174. # i2 = (i1 + 1) % len(polygon)
  175. # This loop assumes the last point is the repeated first point to form closed polygon:
  176. # for i1 in range(len(polygon)-1):
  177. # i2 = (i1 + 1)
  178. # This loop combines both loops above:
  179. for i1 in range(len(polygon)-1*closed):
  180. i2 = (i1 + 1) % len(polygon)
  181. #print("i1={} i2={}".format(polygon[i1], i2))
  182. p1 = polygon[i1]
  183. p2 = polygon[i2]
  184. normal_old = pcbnew.wxPoint(p2[1] - p1[1], p1[0] - p2[0])
  185. minA, maxA, minB, maxB = (None,) * 4
  186. for p in poly_a:
  187. projected = normal[0] * p[0] + normal[1] * p[1]
  188. if not minA or projected < minA:
  189. minA = projected
  190. if not maxA or projected > maxA:
  191. maxA = projected
  192. for p in poly_b:
  193. projected = normal[0] * p[0] + normal[1] * p[1]
  194. if not minB or projected < minB:
  195. minB = projected
  196. if not maxB or projected > maxB:
  197. maxB = projected
  198. #print("maxA={} minB={} -- maxB={} minA={}".format(maxA, minB, maxB, minA))
  199. if maxA < minB or maxB < minA:
  200. return False
  201. #print(" Nope\n")
  202. return True
  203. # To find orientation of ordered triplet (p, q, r).
  204. # The function returns following values
  205. # 0 --> p, q and r are colinear
  206. # 1 --> Clockwise
  207. # 2 --> Counterclockwise
  208. # sign = lambda x: x and (1, -1)[x < 0]
  209. # Get leftmost point
  210. # i, value = min(enumerate(vector), key=attrgetter('x'))
  211. # nextpoint = vector[(i+1)%len(vector)]
  212. # @staticmethod
  213. # def orientation(p, q, r)
  214. # {
  215. # int val = (q[1] - p[1]) * (r[0] - q[0]) -
  216. # (q[0] - p[0]) * (r[1] - q[1]);
  217. # if (val == 0) return 0; // colinear
  218. # return (val > 0)? 1: 2; // clock or counterclock wise
  219. # }
  220. @staticmethod
  221. def convex_hull(self,line,vector):
  222. """NOT IMPLEMENTED.
  223. Currently returns bounding box, best used on orthogonal.
  224. Return the convex hull of point list vector
  225. A fast algorithm using Jarvis's Algorithm (aka Wrapping)."""
  226. minx = vector[0][0]
  227. miny = vector[0][1]
  228. maxx = minx
  229. maxy = miny
  230. for v in vector:
  231. minx = min(minx,v[0])
  232. maxx = max(minx,v[0])
  233. miny = min(miny,v[1])
  234. maxy = max(miny,v[1])
  235. return (
  236. pcbnew.wxPoint(minx,maxy), # upper left
  237. pcbnew.wxPoint(maxx,maxy), # upper right
  238. pcbnew.wxPoint(maxx,miny), # lower right
  239. pcbnew.wxPoint(minx,miny)) # lower left