alignpoints.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. # Copyright 2015 Google Inc. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import math
  15. import numpy as np
  16. from numpy.linalg import lstsq
  17. def alignCorners(glyph, va, subsegments):
  18. out = va.copy()
  19. # for i,c in enumerate(subsegments):
  20. # segmentCount = len(glyph.contours[i].segments) - 1
  21. # n = len(c)
  22. # for j,s in enumerate(c):
  23. # if j < segmentCount:
  24. # seg = glyph.contours[i].segments[j]
  25. # if seg.type == "line":
  26. # subIndex = subsegmentIndex(i,j,subsegments)
  27. # out[subIndex] = alignPoints(va[subIndex])
  28. for i,c in enumerate(subsegments):
  29. segmentCount = len(glyph.contours[i].segments)
  30. n = len(c)
  31. for j,s in enumerate(c):
  32. if j < segmentCount - 1:
  33. segType = glyph.contours[i].segments[j].type
  34. segnextType = glyph.contours[i].segments[j+1].type
  35. next = j+1
  36. elif j == segmentCount -1 and s[1] > 3:
  37. segType = glyph.contours[i].segments[j].type
  38. segNextType = "line"
  39. next = j+1
  40. elif j == segmentCount:
  41. segType = "line"
  42. segnextType = glyph.contours[i].segments[1].type
  43. if glyph.name == "J":
  44. print s[1]
  45. print segnextType
  46. next = 1
  47. else:
  48. break
  49. if segType == "line" and segnextType == "line":
  50. subIndex = subsegmentIndex(i,j,subsegments)
  51. pts = va[subIndex]
  52. ptsnext = va[subsegmentIndex(i,next,subsegments)]
  53. # out[subIndex[-1]] = (out[subIndex[-1]] - 500) * 3 + 500 #findCorner(pts, ptsnext)
  54. # print subIndex[-1], subIndex, subsegmentIndex(i,next,subsegments)
  55. try:
  56. out[subIndex[-1]] = findCorner(pts, ptsnext)
  57. except:
  58. pass
  59. # print glyph.name, "Can't find corner: parallel lines"
  60. return out
  61. def subsegmentIndex(contourIndex, segmentIndex, subsegments):
  62. # This whole thing is so dumb. Need a better data model for subsegments
  63. contourOffset = 0
  64. for i,c in enumerate(subsegments):
  65. if i == contourIndex:
  66. break
  67. contourOffset += c[-1][0]
  68. n = subsegments[contourIndex][-1][0]
  69. # print contourIndex, contourOffset, n
  70. startIndex = subsegments[contourIndex][segmentIndex-1][0]
  71. segmentCount = subsegments[contourIndex][segmentIndex][1]
  72. endIndex = (startIndex + segmentCount + 1) % (n)
  73. indices = np.array([(startIndex + i) % (n) + contourOffset for i in range(segmentCount + 1)])
  74. return indices
  75. def alignPoints(pts, start=None, end=None):
  76. if start == None or end == None:
  77. start, end = fitLine(pts)
  78. out = pts.copy()
  79. for i,p in enumerate(pts):
  80. out[i] = nearestPoint(start, end, p)
  81. return out
  82. def findCorner(pp, nn):
  83. if len(pp) < 4 or len(nn) < 4:
  84. assert 0, "line too short to fit"
  85. pStart,pEnd = fitLine(pp)
  86. nStart,nEnd = fitLine(nn)
  87. prev = pEnd - pStart
  88. next = nEnd - nStart
  89. # print int(np.arctan2(prev[1],prev[0]) / math.pi * 180),
  90. # print int(np.arctan2(next[1],next[0]) / math.pi * 180)
  91. # if lines are parallel, return simple average of end and start points
  92. if np.dot(prev / np.linalg.norm(prev),
  93. next / np.linalg.norm(next)) > .999999:
  94. # print "parallel lines", np.arctan2(prev[1],prev[0]), np.arctan2(next[1],next[0])
  95. # print prev, next
  96. assert 0, "parallel lines"
  97. return lineIntersect(pStart, pEnd, nStart, nEnd)
  98. def lineIntersect((x1,y1),(x2,y2),(x3,y3),(x4,y4)):
  99. x12 = x1 - x2
  100. x34 = x3 - x4
  101. y12 = y1 - y2
  102. y34 = y3 - y4
  103. det = x12 * y34 - y12 * x34
  104. if det == 0:
  105. print "parallel!"
  106. a = x1 * y2 - y1 * x2
  107. b = x3 * y4 - y3 * x4
  108. x = (a * x34 - b * x12) / det
  109. y = (a * y34 - b * y12) / det
  110. return (x,y)
  111. def fitLineLSQ(pts):
  112. "returns a line fit with least squares. Fails for vertical lines"
  113. n = len(pts)
  114. a = np.ones((n,2))
  115. for i in range(n):
  116. a[i,0] = pts[i,0]
  117. line = lstsq(a,pts[:,1])[0]
  118. return line
  119. def fitLine(pts):
  120. """returns a start vector and direction vector
  121. Assumes points segments that already form a somewhat smooth line
  122. """
  123. n = len(pts)
  124. if n < 1:
  125. return (0,0),(0,0)
  126. a = np.zeros((n-1,2))
  127. for i in range(n-1):
  128. v = pts[i] - pts[i+1]
  129. a[i] = v / np.linalg.norm(v)
  130. direction = np.mean(a[1:-1], axis=0)
  131. start = np.mean(pts[1:-1], axis=0)
  132. return start, start+direction
  133. def nearestPoint(a,b,c):
  134. "nearest point to point c on line a_b"
  135. magnitude = np.linalg.norm(b-a)
  136. if magnitude == 0:
  137. raise Exception, "Line segment cannot be 0 length"
  138. return (b-a) * np.dot((c-a) / magnitude, (b-a) / magnitude) + a
  139. # pts = np.array([[1,1],[2,2],[3,3],[4,4]])
  140. # pts2 = np.array([[1,0],[2,0],[3,0],[4,0]])
  141. # print alignPoints(pts2, start = pts[0], end = pts[0]+pts[0])
  142. # # print findCorner(pts,pts2)