IkConstraint.lua 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. -------------------------------------------------------------------------------
  2. -- Spine Runtimes Software License v2.5
  3. --
  4. -- Copyright (c) 2013-2016, Esoteric Software
  5. -- All rights reserved.
  6. --
  7. -- You are granted a perpetual, non-exclusive, non-sublicensable, and
  8. -- non-transferable license to use, install, execute, and perform the Spine
  9. -- Runtimes software and derivative works solely for personal or internal
  10. -- use. Without the written permission of Esoteric Software (see Section 2 of
  11. -- the Spine Software License Agreement), you may not (a) modify, translate,
  12. -- adapt, or develop new applications using the Spine Runtimes or otherwise
  13. -- create derivative works or improvements of the Spine Runtimes or (b) remove,
  14. -- delete, alter, or obscure any trademarks or any copyright, trademark, patent,
  15. -- or other intellectual property or proprietary rights notices on or in the
  16. -- Software, including any copy thereof. Redistributions in binary or source
  17. -- form must include this license and terms.
  18. --
  19. -- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
  20. -- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. -- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  22. -- EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24. -- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
  25. -- USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  26. -- IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. -- POSSIBILITY OF SUCH DAMAGE.
  29. -------------------------------------------------------------------------------
  30. local setmetatable = setmetatable
  31. local math_pi = math.pi
  32. local math_atan2 = math.atan2
  33. local math_sqrt = math.sqrt
  34. local math_acos = math.acos
  35. local math_sin = math.sin
  36. local math_cos = math.cos
  37. local table_insert = table.insert
  38. local math_deg = math.deg
  39. local math_rad = math.rad
  40. local math_abs = math.abs
  41. local IkConstraint = {}
  42. IkConstraint.__index = IkConstraint
  43. function IkConstraint.new (data, skeleton)
  44. if not data then error("data cannot be nil", 2) end
  45. if not skeleton then error("skeleton cannot be nil", 2) end
  46. local self = {
  47. data = data,
  48. bones = {},
  49. target = nil,
  50. mix = data.mix,
  51. compress = data.compress,
  52. stretch = data.stretch,
  53. bendDirection = data.bendDirection,
  54. }
  55. setmetatable(self, IkConstraint)
  56. local self_bones = self.bones
  57. for _,boneData in ipairs(data.bones) do
  58. table_insert(self_bones, skeleton:findBone(boneData.name))
  59. end
  60. self.target = skeleton:findBone(data.target.name)
  61. return self
  62. end
  63. function IkConstraint:apply ()
  64. self:update()
  65. end
  66. function IkConstraint:update ()
  67. local target = self.target
  68. local bones = self.bones
  69. local boneCount = #bones
  70. if boneCount == 1 then
  71. self:apply1(bones[1], target.worldX, target.worldY, self.compress, self.stretch, self.data.uniform, self.mix)
  72. elseif boneCount == 2 then
  73. self:apply2(bones[1], bones[2], target.worldX, target.worldY, self.bendDirection, self.stretch, self.mix)
  74. end
  75. end
  76. function IkConstraint:apply1 (bone, targetX, targetY, compress, stretch, uniform, alpha)
  77. if not bone.appliedValid then bone:updateAppliedTransform() end
  78. local p = bone.parent
  79. local id = 1 / (p.a * p.d - p.b * p.c)
  80. local x = targetX - p.worldX
  81. local y = targetY - p.worldY
  82. local tx = (x * p.d - y * p.b) * id - bone.ax
  83. local ty = (y * p.a - x * p.c) * id - bone.ay
  84. local rotationIK = math_deg(math_atan2(ty, tx)) - bone.ashearX - bone.arotation
  85. if bone.ascaleX < 0 then rotationIK = rotationIK + 180 end
  86. if rotationIK > 180 then
  87. rotationIK = rotationIK - 360
  88. elseif (rotationIK < -180) then
  89. rotationIK = rotationIK + 360
  90. end
  91. local sx = bone.ascaleX
  92. local sy = bone.ascaleY
  93. if compress or stretch then
  94. local b = bone.data.length * sx
  95. local dd = math_sqrt(tx * tx + ty * ty)
  96. if (compress and dd < b) or (stretch and dd > b) and b > 0.0001 then
  97. local s = (dd / b - 1) * alpha + 1
  98. sx = sx * s
  99. if uniform then sy = sy * s end
  100. end
  101. end
  102. bone:updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY)
  103. end
  104. function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, alpha)
  105. if alpha == 0 then
  106. child:updateWorldTransform()
  107. return
  108. end
  109. if not parent.appliedValid then parent:updateAppliedTransform() end
  110. if not child.appliedValid then child:updateAppliedTransform() end
  111. local px = parent.ax
  112. local py = parent.ay
  113. local psx = parent.ascaleX
  114. local sx = psx
  115. local psy = parent.ascaleY
  116. local csx = child.ascaleX
  117. local os1 = 0
  118. local os2 = 0
  119. local s2 = 0
  120. if psx < 0 then
  121. psx = -psx
  122. os1 = 180
  123. s2 = -1
  124. else
  125. os1 = 0
  126. s2 = 1
  127. end
  128. if psy < 0 then
  129. psy = -psy
  130. s2 = -s2
  131. end
  132. if csx < 0 then
  133. csx = -csx
  134. os2 = 180
  135. else
  136. os2 = 0
  137. end
  138. local cx = child.ax
  139. local cy = 0
  140. local cwx = 0
  141. local cwy = 0
  142. local a = parent.a
  143. local b = parent.b
  144. local c = parent.c
  145. local d = parent.d
  146. local u = math_abs(psx - psy) <= 0.0001
  147. if not u then
  148. cy = 0
  149. cwx = a * cx + parent.worldX
  150. cwy = c * cx + parent.worldY
  151. else
  152. cy = child.ay
  153. cwx = a * cx + b * cy + parent.worldX
  154. cwy = c * cx + d * cy + parent.worldY
  155. end
  156. local pp = parent.parent
  157. a = pp.a
  158. b = pp.b
  159. c = pp.c
  160. d = pp.d
  161. local id = 1 / (a * d - b * c)
  162. local x = targetX - pp.worldX
  163. local y = targetY - pp.worldY
  164. local tx = (x * d - y * b) * id - px
  165. local ty = (y * a - x * c) * id - py
  166. local dd = tx * tx + ty * ty
  167. x = cwx - pp.worldX
  168. y = cwy - pp.worldY
  169. local dx = (x * d - y * b) * id - px
  170. local dy = (y * a - x * c) * id - py
  171. local l1 = math_sqrt(dx * dx + dy * dy)
  172. local l2 = child.data.length * csx
  173. local a1 = 0
  174. local a2 = 0
  175. if u then
  176. l2 = l2 * psx
  177. local cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2)
  178. if cos < -1 then
  179. cos = -1
  180. elseif cos > 1 then
  181. cos = 1
  182. if stretch then sx = sx * ((math_sqrt(dd) / (l1 + l2) - 1) * alpha + 1) end
  183. end
  184. a2 = math_acos(cos) * bendDir
  185. a = l1 + l2 * cos
  186. b = l2 * math_sin(a2)
  187. a1 = math_atan2(ty * a - tx * b, tx * a + ty * b)
  188. else
  189. local skip = false
  190. a = psx * l2
  191. b = psy * l2
  192. local aa = a * a
  193. local bb = b * b
  194. local ta = math_atan2(ty, tx);
  195. c = bb * l1 * l1 + aa * dd - aa * bb
  196. local c1 = -2 * bb * l1
  197. local c2 = bb - aa
  198. d = c1 * c1 - 4 * c2 * c
  199. if d >= 0 then
  200. local q = math_sqrt(d);
  201. if (c1 < 0) then q = -q end
  202. q = -(c1 + q) / 2
  203. local r0 = q / c2
  204. local r1 = c / q
  205. local r = r1
  206. if math_abs(r0) < math_abs(r1) then r = r0 end
  207. if r * r <= dd then
  208. y = math_sqrt(dd - r * r) * bendDir
  209. a1 = ta - math_atan2(y, r)
  210. a2 = math_atan2(y / psy, (r - l1) / psx)
  211. skip = true
  212. end
  213. end
  214. if not skip then
  215. local minAngle = math_pi
  216. local minX = l1 - a
  217. local minDist = minX * minX
  218. local minY = 0;
  219. local maxAngle = 0
  220. local maxX = l1 + a
  221. local maxDist = maxX * maxX
  222. local maxY = 0
  223. c = -a * l1 / (aa - bb)
  224. if (c >= -1 and c <= 1) then
  225. c = math_acos(c)
  226. x = a * math_cos(c) + l1
  227. y = b * math_sin(c)
  228. d = x * x + y * y
  229. if d < minDist then
  230. minAngle = c
  231. minDist = d
  232. minX = x
  233. minY = y
  234. end
  235. if d > maxDist then
  236. maxAngle = c
  237. maxDist = d
  238. maxX = x
  239. maxY = y
  240. end
  241. end
  242. if dd <= (minDist + maxDist) / 2 then
  243. a1 = ta - math_atan2(minY * bendDir, minX)
  244. a2 = minAngle * bendDir
  245. else
  246. a1 = ta - math_atan2(maxY * bendDir, maxX)
  247. a2 = maxAngle * bendDir
  248. end
  249. end
  250. end
  251. local os = math_atan2(cy, cx) * s2
  252. local rotation = parent.arotation
  253. a1 = math_deg(a1 - os) + os1 - rotation
  254. if a1 > 180 then
  255. a1 = a1 - 360
  256. elseif a1 < -180 then
  257. a1 = a1 + 360
  258. end
  259. parent:updateWorldTransformWith(px, py, rotation + a1 * alpha, sx, parent.ascaleY, 0, 0)
  260. rotation = child.rotation
  261. a2 = (math_deg(a2 + os) - child.ashearX) * s2 + os2 - rotation
  262. if a2 > 180 then
  263. a2 = a2 - 360
  264. elseif a2 < -180 then
  265. a2 = a2 + 360
  266. end
  267. child:updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
  268. end
  269. return IkConstraint