TransformConstraint.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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 utils = require "spine-lua.utils"
  32. local math_pi = math.pi
  33. local math_pi2 = math.pi * 2
  34. local math_atan2 = math.atan2
  35. local math_sqrt = math.sqrt
  36. local math_acos = math.acos
  37. local math_sin = math.sin
  38. local math_cos = math.cos
  39. local table_insert = table.insert
  40. local math_deg = math.deg
  41. local math_rad = math.rad
  42. local math_abs = math.abs
  43. local TransformConstraint = {}
  44. TransformConstraint.__index = TransformConstraint
  45. function TransformConstraint.new (data, skeleton)
  46. if not data then error("data cannot be nil", 2) end
  47. if not skeleton then error("skeleton cannot be nil", 2) end
  48. local self = {
  49. data = data,
  50. bones = {},
  51. target = nil,
  52. rotateMix = data.rotateMix, translateMix = data.translateMix, scaleMix = data.scaleMix, shearMix = data.shearMix,
  53. temp = { 0, 0 }
  54. }
  55. setmetatable(self, TransformConstraint)
  56. for i,bone in ipairs(data.bones) do
  57. table_insert(self.bones, skeleton:findBone(bone.name))
  58. end
  59. self.target = skeleton:findBone(data.target.name)
  60. return self
  61. end
  62. function TransformConstraint:apply ()
  63. self:update()
  64. end
  65. function TransformConstraint:update ()
  66. if self.data.local_ then
  67. if self.data.relative then
  68. self:applyRelativeLocal()
  69. else
  70. self:applyAbsoluteLocal()
  71. end
  72. else
  73. if self.data.relative then
  74. self:applyRelativeWorld()
  75. else
  76. self:applyAbsoluteWorld()
  77. end
  78. end
  79. end
  80. function TransformConstraint:applyAbsoluteWorld ()
  81. local rotateMix = self.rotateMix
  82. local translateMix = self.translateMix
  83. local scaleMix = self.scaleMix
  84. local shearMix = self.shearMix
  85. local target = self.target
  86. local ta = target.a
  87. local tb = target.b
  88. local tc = target.c
  89. local td = target.d
  90. local degRadReflect = 0;
  91. if ta * td - tb * tc > 0 then degRadReflect = utils.degRad else degRadReflect = -utils.degRad end
  92. local offsetRotation = self.data.offsetRotation * degRadReflect
  93. local offsetShearY = self.data.offsetShearY * degRadReflect
  94. local bones = self.bones
  95. for i, bone in ipairs(bones) do
  96. local modified = false
  97. if rotateMix ~= 0 then
  98. local a = bone.a
  99. local b = bone.b
  100. local c = bone.c
  101. local d = bone.d
  102. local r = math_atan2(tc, ta) - math_atan2(c, a) + offsetRotation
  103. if r > math_pi then
  104. r = r - math_pi2
  105. elseif r < -math_pi then
  106. r = r + math_pi2
  107. end
  108. r = r * rotateMix
  109. local cos = math_cos(r)
  110. local sin = math_sin(r)
  111. bone.a = cos * a - sin * c
  112. bone.b = cos * b - sin * d
  113. bone.c = sin * a + cos * c
  114. bone.d = sin * b + cos * d
  115. modified = true
  116. end
  117. if translateMix ~= 0 then
  118. local temp = self.temp
  119. temp[1] = self.data.offsetX
  120. temp[2] = self.data.offsetY
  121. target:localToWorld(temp)
  122. bone.worldX = bone.worldX + (temp[1] - bone.worldX) * translateMix
  123. bone.worldY = bone.worldY + (temp[2] - bone.worldY) * translateMix
  124. modified = true
  125. end
  126. if scaleMix > 0 then
  127. local s = math_sqrt(bone.a * bone.a + bone.c * bone.c)
  128. local ts = math_sqrt(ta * ta + tc * tc)
  129. if s > 0.00001 then
  130. s = (s + (ts - s + self.data.offsetScaleX) * scaleMix) / s
  131. end
  132. bone.a = bone.a * s
  133. bone.c = bone.c * s
  134. s = math_sqrt(bone.b * bone.b + bone.d * bone.d)
  135. ts = math_sqrt(tb * tb + td * td)
  136. if s > 0.00001 then
  137. s = (s + (ts - s + self.data.offsetScaleY) * scaleMix) / s
  138. end
  139. bone.b = bone.b * s
  140. bone.d = bone.d * s
  141. modified = true
  142. end
  143. if shearMix > 0 then
  144. local b = bone.b
  145. local d = bone.d
  146. local by = math_atan2(d, b)
  147. local r = math_atan2(td, tb) - math_atan2(tc, ta) - (by - math_atan2(bone.c, bone.a))
  148. if r > math_pi then
  149. r = r - math_pi2
  150. elseif r < -math_pi then
  151. r = r + math_pi2
  152. end
  153. r = by + (r + offsetShearY) * shearMix
  154. local s = math_sqrt(b * b + d * d)
  155. bone.b = math_cos(r) * s
  156. bone.d = math_sin(r) * s
  157. modified = true
  158. end
  159. if modified then bone.appliedValid = false end
  160. end
  161. end
  162. function TransformConstraint:applyRelativeWorld ()
  163. local rotateMix = self.rotateMix
  164. local translateMix = self.translateMix
  165. local scaleMix = self.scaleMix
  166. local shearMix = self.shearMix
  167. local target = self.target
  168. local ta = target.a
  169. local tb = target.b
  170. local tc = target.c
  171. local td = target.d
  172. local degRadReflect = 0;
  173. if ta * td - tb * tc > 0 then degRadReflect = utils.degRad else degRadReflect = -utils.degRad end
  174. local offsetRotation = self.data.offsetRotation * degRadReflect
  175. local offsetShearY = self.data.offsetShearY * degRadReflect
  176. local bones = self.bones
  177. for i, bone in ipairs(bones) do
  178. local modified = false
  179. if rotateMix ~= 0 then
  180. local a = bone.a
  181. local b = bone.b
  182. local c = bone.c
  183. local d = bone.d
  184. local r = math_atan2(tc, ta) + offsetRotation
  185. if r > math_pi then
  186. r = r - math_pi2
  187. elseif r < -math_pi then
  188. r = r + math_pi2
  189. end
  190. r = r * rotateMix
  191. local cos = math_cos(r)
  192. local sin = math_sin(r)
  193. bone.a = cos * a - sin * c
  194. bone.b = cos * b - sin * d
  195. bone.c = sin * a + cos * c
  196. bone.d = sin * b + cos * d
  197. modified = true
  198. end
  199. if translateMix ~= 0 then
  200. local temp = self.temp
  201. temp[1] = self.data.offsetX
  202. temp[2] = self.data.offsetY
  203. target:localToWorld(temp)
  204. bone.worldX = bone.worldX + temp[1] * translateMix
  205. bone.worldY = bone.worldY + temp[2] * translateMix
  206. modified = true
  207. end
  208. if scaleMix > 0 then
  209. local s = (math_sqrt(ta * ta + tc * tc) - 1 + self.data.offsetScaleX) * scaleMix + 1
  210. bone.a = bone.a * s
  211. bone.c = bone.c * s
  212. local s = (math_sqrt(tb * tb + td * td) - 1 + self.data.offsetScaleY) * scaleMix + 1
  213. bone.b = bone.b * s
  214. bone.d = bone.d * s
  215. modified = true
  216. end
  217. if shearMix > 0 then
  218. local r = math_atan2(td, tb) - math_atan2(tc, ta)
  219. if r > math_pi then
  220. r = r - math_pi2
  221. elseif r < -math_pi then
  222. r = r + math_pi2
  223. end
  224. local b = bone.b
  225. local d = bone.d
  226. r = math_atan2(d, b) + (r - math_pi / 2 + offsetShearY) * shearMix;
  227. local s = math_sqrt(b * b + d * d)
  228. bone.b = math_cos(r) * s
  229. bone.d = math_sin(r) * s
  230. modified = true
  231. end
  232. if modified then bone.appliedValid = false end
  233. end
  234. end
  235. function TransformConstraint:applyAbsoluteLocal ()
  236. local rotateMix = self.rotateMix
  237. local translateMix = self.translateMix
  238. local scaleMix = self.scaleMix
  239. local shearMix = self.shearMix
  240. local target = self.target
  241. if not target.appliedValid then target:updatedAppliedTransform() end
  242. local bones = self.bones
  243. for i, bone in ipairs(bones) do
  244. local modified = false
  245. if not bone.appliedValid then bone:updateAppliedTransform() end
  246. local rotation = bone.arotation
  247. if rotateMix ~= 0 then
  248. local r = target.arotation - rotation + self.data.offsetRotation
  249. r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
  250. rotation = rotation + r * rotateMix
  251. end
  252. local x = bone.ax
  253. local y = bone.ay
  254. if translateMix ~= 0 then
  255. x = x + (target.ax - x + self.data.offsetX) * translateMix
  256. y = x + (target.ay - y + self.data.offsetY) * translateMix
  257. end
  258. local scaleX = bone.ascaleX
  259. local scaleY = bone.ascaleY
  260. if scaleMix > 0 then
  261. if scaleX > 0.00001 then
  262. scaleX = (scaleX + (target.ascaleX - scaleX + self.data.offsetScaleX) * scaleMix) / scaleX
  263. end
  264. if scaleY > 0.00001 then
  265. scaleY = (scaleY + (target.ascaleY - scaleY + self.data.offsetScaleY) * scaleMix) / scaleY
  266. end
  267. end
  268. local shearY = bone.ashearY
  269. if shearMix > 0 then
  270. local r = target.ashearY - shearY + self.data.offsetShearY
  271. r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
  272. bone.shearY = bone.shearY + r * shearMix
  273. end
  274. bone:updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
  275. end
  276. end
  277. function TransformConstraint:applyRelativeLocal ()
  278. local rotateMix = self.rotateMix
  279. local translateMix = self.translateMix
  280. local scaleMix = self.scaleMix
  281. local shearMix = self.shearMix
  282. local target = self.target
  283. if not target.appliedValid then target:updateAppliedTransform() end
  284. local bones = self.bones
  285. for i, bone in ipairs(bones) do
  286. if not bone.appliedValid then bone:updateAppliedTransform() end
  287. local rotation = bone.arotation
  288. if rotateMix ~= 0 then rotation = rotation + (target.arotation + self.data.offsetRotation) * rotateMix end
  289. local x = bone.ax
  290. local y = bone.ay
  291. if translateMix ~= 0 then
  292. x = x + (target.ax + self.data.offsetX) * translateMix
  293. y = y + (target.ay + self.data.offsetY) * translateMix
  294. end
  295. local scaleX = bone.ascaleX
  296. local scaleY = bone.ascaleY
  297. if scaleMix > 0 then
  298. if scaleX > 0.00001 then
  299. scaleX = scaleX * (((target.ascaleX - 1 + self.data.offsetScaleX) * scaleMix) + 1)
  300. end
  301. if scaleY > 0.00001 then
  302. scaleY = scaleY * (((target.ascaleY - 1 + this.data.offsetScaleY) * scaleMix) + 1)
  303. end
  304. end
  305. local shearY = bone.ashearY
  306. if shearMix > 0 then shearY = shearY + (target.ashearY + self.data.offsetShearY) * shearMix end
  307. bone:updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY)
  308. end
  309. end
  310. return TransformConstraint