morph.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. function init_morph() {
  2. function load_data(path) {
  3. var deferred = new $.Deferred();
  4. var oReq = new XMLHttpRequest();
  5. oReq.open("GET", path, true);
  6. oReq.responseType = "arraybuffer";
  7. oReq.onload = function (oEvent) {
  8. var arrayBuffer = oReq.response; // Note: not oReq.responseText
  9. if (arrayBuffer) {
  10. var data = new Float32Array(arrayBuffer);
  11. deferred.resolve(data);
  12. }else{
  13. deferred.reject();
  14. }
  15. };
  16. oReq.send(null);
  17. return deferred.promise();
  18. }
  19. function load_targets(path) {
  20. var deferred = new $.Deferred();
  21. var oReq = new XMLHttpRequest();
  22. oReq.open("GET", path, true);
  23. oReq.responseType = "text";
  24. oReq.onload = function (oEvent) {
  25. if (this.readyState == 4 && this.status == 200) {
  26. var target_set = JSON.parse(this.responseText);
  27. var shape = target_set[0]["shape"];
  28. var scale = target_set[0]["scale"];
  29. load_data(target_set[0]["path"]).then(function(data){
  30. var target_contours = {};
  31. for(var cls = 0; cls < shape[0]; ++cls)
  32. {
  33. var contour = [];
  34. for(var length = 0; length < shape[1]; ++length)
  35. {
  36. var index = (cls*shape[1] + length)*shape[2];
  37. contour.push(data.slice(index, index+2));
  38. }
  39. target_contours[cls + 1] = contour;
  40. }
  41. deferred.resolve(target_contours, scale);
  42. }, function(){
  43. deferred.reject();
  44. });
  45. }else{
  46. deferred.reject();
  47. }
  48. };
  49. oReq.send(null);
  50. return deferred.promise();
  51. }
  52. load_targets("morph_data/desc.json").then(on_ready);
  53. function mod(v, n) {
  54. return ((v%n)+n)%n;
  55. };
  56. function find_a_center(points) {
  57. var sumx = 0;
  58. for(var i = 0;i<points.length;++i){
  59. sumx += points[i][0];
  60. }
  61. var cx = sumx/points.length;
  62. var crosses = [];
  63. for (var i = 0;i<points.length - 1; ++i) {
  64. if ( (points[i][0] < cx && cx <= points[i + 1][0]) || (points[i][0] >= cx && cx > points[i + 1][0]) )
  65. crosses.push((points[i][1] + points[i + 1][1]) * 0.5);
  66. }
  67. crosses.sort(function(a, b){return a - b;});
  68. var max_value = 0;
  69. var max_index = 0;
  70. for (var i = 0;i<crosses.length/2;++i){
  71. var v = Math.abs(crosses[2 * i] - crosses[2 * i + 1]);
  72. if(v > max_value) {
  73. max_value = v;
  74. max_index = i;
  75. }
  76. }
  77. var cy = (crosses[2 * max_index] + crosses[2 * max_index + 1]) * 0.5;
  78. return [cx, cy]
  79. }
  80. function transform_polar(xys, width, height, scale) {
  81. var stats = {};
  82. width = width || 1.0;
  83. height = height || 1.0;
  84. scale = scale || [1.0, 1.0];
  85. var center = find_a_center(xys);
  86. center[0] = center[0]*width/scale[0];
  87. center[1] = center[1]*height/scale[1];
  88. stats.center = center;
  89. var min_x = width;
  90. var min_y = height;
  91. var max_xy = 0;
  92. var imin_x = 0;
  93. var imin_y = 0;
  94. var imax_xy = 0;
  95. var R = [];
  96. var T = [];
  97. var X = [];
  98. var Y = [];
  99. for(var i = 0;i<xys.length;++i) {
  100. let nx = ((xys[i][0])*width/scale[0]) - center[0];
  101. let ny = ((xys[i][1])*height/scale[1]) - center[1];
  102. R.push(Math.sqrt(nx*nx + ny*ny));
  103. T.push(Math.atan2(ny, nx));
  104. X.push(nx);
  105. Y.push(ny);
  106. if(nx < min_x) {
  107. imin_x = i;
  108. min_x = nx;
  109. }
  110. if(ny < min_y) {
  111. imin_y = i;
  112. min_y = ny;
  113. }
  114. if(max_xy < nx + ny) {
  115. imax_xy = i;
  116. max_xy = nx + ny;
  117. }
  118. }
  119. stats.X = X;
  120. stats.Y = Y;
  121. stats.R = R;
  122. stats.T = T;
  123. stats.min_x = imin_x;
  124. stats.min_y = imin_y;
  125. stats.max_xy = imax_xy;
  126. return stats;
  127. }
  128. function extract_extrema(Rs, start, end) {
  129. if(end < start) end = end + Rs.length;
  130. var out = [];
  131. var g = 1;
  132. out.push(start);
  133. for(var i = start+1;i<end;++i) {
  134. let _i = mod(i, Rs.length);
  135. let ll = Rs[mod(_i - g - g, Rs.length)];
  136. let l = Rs[mod(_i - g, Rs.length)];
  137. let m = Rs[_i];
  138. let r = Rs[mod(_i + g, Rs.length)];
  139. let rr = Rs[mod(_i + g + g, Rs.length)];
  140. if(l < m && r < m && ll < m && rr < m) {
  141. out.push(_i);
  142. }else if(l > m && r > m && ll > m && rr > m){
  143. out.push(_i);
  144. }
  145. }
  146. out.push(mod(end, Rs.length));
  147. return out;
  148. }
  149. function delta_theta(t0, t1) {
  150. if(t0 > t1)
  151. return Math.min(t0-t1, 2*Math.PI - t0 + t1);
  152. else
  153. return Math.min(t1-t0, 2*Math.PI - t1 + t0);
  154. }
  155. function interpolate_theta(t0, t1, alpha) {
  156. var a0 = 1-alpha;
  157. var a1 = alpha;
  158. if(t1 > t0){
  159. if(t1 - t0 < Math.PI*2 + t0 - t1) return t0*a0 + t1*a1;
  160. else return (t0+Math.PI*2)*a0 + t1*a1;
  161. }else{
  162. if(t0 - t1 < Math.PI*2 + t1 - t0) return t0*a0 + t1*a1;
  163. else return t0*a0 + (t1+Math.PI*2)*a1;
  164. }
  165. }
  166. function DTW(iN, nstat, iM, mstat) {
  167. var table = [];
  168. var visit = [];
  169. for(var i = 0;i<iN.length;++i) {
  170. table[i] = [Infinity];
  171. visit[i] = [-1];
  172. }
  173. for(var j = 0;j<iM.length;++j) {
  174. table[0][j] = Infinity;
  175. visit[0][j] = -1;
  176. }
  177. table[0][0] = 0;
  178. visit[0][0] = -1;
  179. RN = nstat.R;
  180. RM = mstat.R;
  181. TN = nstat.T;
  182. TM = mstat.T;
  183. for(var i = 1;i<iN.length;++i){
  184. for(var j = 1;j<iM.length;++j) {
  185. let dR = Math.abs(RN[iN[i]], RM[iM[j]]);
  186. let dT = delta_theta(TN[iN[i]], TM[iM[j]]);
  187. var cost = dR + dT;
  188. let _10 = table[i-1][j];
  189. let _01 = table[i][j-1];
  190. let _11 = table[i-1][j-1];
  191. if(_10 < _01) {
  192. if(_10 < _11) {
  193. table[i][j] = cost*2 + _10;
  194. visit[i][j] = 10;
  195. }else{
  196. table[i][j] = cost + _11;
  197. visit[i][j] = 11;
  198. }
  199. }else{
  200. if(_01 < _11) {
  201. table[i][j] = cost*2 + _01;
  202. visit[i][j] = 1;
  203. }else{
  204. table[i][j] = cost + _11;
  205. visit[i][j] = 11;
  206. }
  207. }
  208. }
  209. }
  210. var i = iN.length-1;
  211. var j = iM.length-1;
  212. var map = [];
  213. var v = null;
  214. do {
  215. var v = visit[i][j];
  216. map.unshift([iN[i], iM[j]]);
  217. if(v === 10) {
  218. i = i-1;
  219. }else if(v === 11) {
  220. i = i-1;
  221. j = j-1;
  222. }else{
  223. j = j-1;
  224. }
  225. }while(v !== -1);
  226. return map;
  227. }
  228. function interpolate(C, map, stats, tstats) {
  229. function _ov(f, t, L) {
  230. return t >= f? (t-f): (t + L - f);
  231. }
  232. function _fi(f, t, L, s) {
  233. if(t >= f) {
  234. return f*(1-s) + s*t;
  235. }
  236. let k = f*(1-s) + s*(t + L);
  237. return k >= L? k-L: k;
  238. }
  239. function _i(V, a, L) {
  240. let f = mod(parseInt(Math.floor(a)), L);
  241. let t = mod(parseInt(Math.ceil(a)), L);
  242. let s = a - f;
  243. return V[f]*(1-s) + V[t]*s;
  244. }
  245. var L = stats.R.length;
  246. for(var i = 0;i<map.length-1;++i) {
  247. var f = map[i];
  248. var t = map[i + 1];
  249. var s = _ov(f[0], t[0], L);
  250. var st = _ov(f[1], t[1], L);
  251. var steps = Math.ceil((s + st)*1.0);
  252. for(var j = 0;j<steps;++j) {
  253. let s = 1.0*j/steps;
  254. let _0 = _fi(f[0], t[0], L, s);
  255. let _1 = _fi(f[1], t[1], L, s);
  256. let x_ = _i(stats.X, _0, L);
  257. let xt_ = _i(tstats.X, _1, L);
  258. let y_ = _i(stats.Y, _0, L);
  259. let yt_ = _i(tstats.Y, _1, L);
  260. let r_ = Math.sqrt(x_*x_ + y_*y_);
  261. let rt_ = Math.sqrt(xt_*xt_ + yt_*yt_);
  262. let t_ = Math.atan2(y_, x_);
  263. let tt_ = Math.atan2(yt_, xt_);
  264. C.push([r_, rt_, t_, tt_]);
  265. }
  266. }
  267. }
  268. function match(stats, tstats) {
  269. var S0 = extract_extrema(stats.R, stats.min_y, stats.min_x);
  270. var tS0 = extract_extrema(tstats.R, tstats.min_y, tstats.min_x);
  271. var map0 = DTW(S0, stats, tS0, tstats);
  272. var S1 = extract_extrema(stats.R, stats.min_x, stats.max_xy);
  273. var tS1 = extract_extrema(tstats.R, tstats.min_x, tstats.max_xy);
  274. var map1 = DTW(S1, stats, tS1, tstats);
  275. var S2 = extract_extrema(stats.R, stats.max_xy, stats.min_y);
  276. var tS2 = extract_extrema(tstats.R, tstats.max_xy, tstats.min_y);
  277. var map2 = DTW(S2, stats, tS2, tstats);
  278. var map = [];
  279. interpolate(map, map0, stats, tstats);
  280. interpolate(map, map1, stats, tstats);
  281. interpolate(map, map2, stats, tstats);
  282. return map;
  283. }
  284. function on_ready(target_contours, scale) {
  285. window.prepare_morph = function(nps, target_id, x, y, width, height, on_ready) {
  286. var target = target_contours[target_id];
  287. var tstats = transform_polar(target, width, height, scale);
  288. var stats = transform_polar(nps);
  289. // map is a sequence of (R, tR, T, tT) followed the contour's order.
  290. var map = match(stats, tstats);
  291. var get_contours = function(step) {
  292. var s = (Math.sin(step * Math.PI / 2) + 1)*0.5;
  293. var u = 1 - Math.exp(-(step + 1)*1.5);
  294. var out = [];
  295. for(var i = 0;i<map.length;++i) {
  296. let tuple = map[i];
  297. let r_ = tuple[0]*(1-s) + tuple[1]*s;
  298. let t_ = interpolate_theta(tuple[2], tuple[3], u);
  299. let x_ = x + r_ * Math.cos(t_) + stats.center[0] * (1-s) + s * tstats.center[0];
  300. let y_ = y + r_ * Math.sin(t_) + stats.center[1] * (1-s) + s * tstats.center[1];
  301. out.push([x_, y_]);
  302. }
  303. return out;
  304. };
  305. on_ready(get_contours);
  306. };
  307. };
  308. };