TrimModifier.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. import {
  2. extendPrototype,
  3. } from '../functionExtensions';
  4. import PropertyFactory from '../PropertyFactory';
  5. import shapePool from '../pooling/shape_pool';
  6. import bez from '../bez';
  7. import {
  8. ShapeModifier,
  9. } from './ShapeModifiers';
  10. import segmentsLengthPool from '../pooling/segments_length_pool';
  11. function TrimModifier() {
  12. }
  13. extendPrototype([ShapeModifier], TrimModifier);
  14. TrimModifier.prototype.initModifierProperties = function (elem, data) {
  15. this.s = PropertyFactory.getProp(elem, data.s, 0, 0.01, this);
  16. this.e = PropertyFactory.getProp(elem, data.e, 0, 0.01, this);
  17. this.o = PropertyFactory.getProp(elem, data.o, 0, 0, this);
  18. this.sValue = 0;
  19. this.eValue = 0;
  20. this.getValue = this.processKeys;
  21. this.m = data.m;
  22. this._isAnimated = !!this.s.effectsSequence.length || !!this.e.effectsSequence.length || !!this.o.effectsSequence.length;
  23. };
  24. TrimModifier.prototype.addShapeToModifier = function (shapeData) {
  25. shapeData.pathsData = [];
  26. };
  27. TrimModifier.prototype.calculateShapeEdges = function (s, e, shapeLength, addedLength, totalModifierLength) {
  28. var segments = [];
  29. if (e <= 1) {
  30. segments.push({
  31. s: s,
  32. e: e,
  33. });
  34. } else if (s >= 1) {
  35. segments.push({
  36. s: s - 1,
  37. e: e - 1,
  38. });
  39. } else {
  40. segments.push({
  41. s: s,
  42. e: 1,
  43. });
  44. segments.push({
  45. s: 0,
  46. e: e - 1,
  47. });
  48. }
  49. var shapeSegments = [];
  50. var i;
  51. var len = segments.length;
  52. var segmentOb;
  53. for (i = 0; i < len; i += 1) {
  54. segmentOb = segments[i];
  55. if (!(segmentOb.e * totalModifierLength < addedLength || segmentOb.s * totalModifierLength > addedLength + shapeLength)) {
  56. var shapeS;
  57. var shapeE;
  58. if (segmentOb.s * totalModifierLength <= addedLength) {
  59. shapeS = 0;
  60. } else {
  61. shapeS = (segmentOb.s * totalModifierLength - addedLength) / shapeLength;
  62. }
  63. if (segmentOb.e * totalModifierLength >= addedLength + shapeLength) {
  64. shapeE = 1;
  65. } else {
  66. shapeE = ((segmentOb.e * totalModifierLength - addedLength) / shapeLength);
  67. }
  68. shapeSegments.push([shapeS, shapeE]);
  69. }
  70. }
  71. if (!shapeSegments.length) {
  72. shapeSegments.push([0, 0]);
  73. }
  74. return shapeSegments;
  75. };
  76. TrimModifier.prototype.releasePathsData = function (pathsData) {
  77. var i;
  78. var len = pathsData.length;
  79. for (i = 0; i < len; i += 1) {
  80. segmentsLengthPool.release(pathsData[i]);
  81. }
  82. pathsData.length = 0;
  83. return pathsData;
  84. };
  85. TrimModifier.prototype.processShapes = function (_isFirstFrame) {
  86. var s;
  87. var e;
  88. if (this._mdf || _isFirstFrame) {
  89. var o = (this.o.v % 360) / 360;
  90. if (o < 0) {
  91. o += 1;
  92. }
  93. if (this.s.v > 1) {
  94. s = 1 + o;
  95. } else if (this.s.v < 0) {
  96. s = 0 + o;
  97. } else {
  98. s = this.s.v + o;
  99. }
  100. if (this.e.v > 1) {
  101. e = 1 + o;
  102. } else if (this.e.v < 0) {
  103. e = 0 + o;
  104. } else {
  105. e = this.e.v + o;
  106. }
  107. if (s > e) {
  108. var _s = s;
  109. s = e;
  110. e = _s;
  111. }
  112. s = Math.round(s * 10000) * 0.0001;
  113. e = Math.round(e * 10000) * 0.0001;
  114. this.sValue = s;
  115. this.eValue = e;
  116. } else {
  117. s = this.sValue;
  118. e = this.eValue;
  119. }
  120. var shapePaths;
  121. var i;
  122. var len = this.shapes.length;
  123. var j;
  124. var jLen;
  125. var pathsData;
  126. var pathData;
  127. var totalShapeLength;
  128. var totalModifierLength = 0;
  129. if (e === s) {
  130. for (i = 0; i < len; i += 1) {
  131. this.shapes[i].localShapeCollection.releaseShapes();
  132. this.shapes[i].shape._mdf = true;
  133. this.shapes[i].shape.paths = this.shapes[i].localShapeCollection;
  134. if (this._mdf) {
  135. this.shapes[i].pathsData.length = 0;
  136. }
  137. }
  138. } else if (!((e === 1 && s === 0) || (e === 0 && s === 1))) {
  139. var segments = [];
  140. var shapeData;
  141. var localShapeCollection;
  142. for (i = 0; i < len; i += 1) {
  143. shapeData = this.shapes[i];
  144. // if shape hasn't changed and trim properties haven't changed, cached previous path can be used
  145. if (!shapeData.shape._mdf && !this._mdf && !_isFirstFrame && this.m !== 2) {
  146. shapeData.shape.paths = shapeData.localShapeCollection;
  147. } else {
  148. shapePaths = shapeData.shape.paths;
  149. jLen = shapePaths._length;
  150. totalShapeLength = 0;
  151. if (!shapeData.shape._mdf && shapeData.pathsData.length) {
  152. totalShapeLength = shapeData.totalShapeLength;
  153. } else {
  154. pathsData = this.releasePathsData(shapeData.pathsData);
  155. for (j = 0; j < jLen; j += 1) {
  156. pathData = bez.getSegmentsLength(shapePaths.shapes[j]);
  157. pathsData.push(pathData);
  158. totalShapeLength += pathData.totalLength;
  159. }
  160. shapeData.totalShapeLength = totalShapeLength;
  161. shapeData.pathsData = pathsData;
  162. }
  163. totalModifierLength += totalShapeLength;
  164. shapeData.shape._mdf = true;
  165. }
  166. }
  167. var shapeS = s;
  168. var shapeE = e;
  169. var addedLength = 0;
  170. var edges;
  171. for (i = len - 1; i >= 0; i -= 1) {
  172. shapeData = this.shapes[i];
  173. if (shapeData.shape._mdf) {
  174. localShapeCollection = shapeData.localShapeCollection;
  175. localShapeCollection.releaseShapes();
  176. // if m === 2 means paths are trimmed individually so edges need to be found for this specific shape relative to whoel group
  177. if (this.m === 2 && len > 1) {
  178. edges = this.calculateShapeEdges(s, e, shapeData.totalShapeLength, addedLength, totalModifierLength);
  179. addedLength += shapeData.totalShapeLength;
  180. } else {
  181. edges = [[shapeS, shapeE]];
  182. }
  183. jLen = edges.length;
  184. for (j = 0; j < jLen; j += 1) {
  185. shapeS = edges[j][0];
  186. shapeE = edges[j][1];
  187. segments.length = 0;
  188. if (shapeE <= 1) {
  189. segments.push({
  190. s: shapeData.totalShapeLength * shapeS,
  191. e: shapeData.totalShapeLength * shapeE,
  192. });
  193. } else if (shapeS >= 1) {
  194. segments.push({
  195. s: shapeData.totalShapeLength * (shapeS - 1),
  196. e: shapeData.totalShapeLength * (shapeE - 1),
  197. });
  198. } else {
  199. segments.push({
  200. s: shapeData.totalShapeLength * shapeS,
  201. e: shapeData.totalShapeLength,
  202. });
  203. segments.push({
  204. s: 0,
  205. e: shapeData.totalShapeLength * (shapeE - 1),
  206. });
  207. }
  208. var newShapesData = this.addShapes(shapeData, segments[0]);
  209. if (segments[0].s !== segments[0].e) {
  210. if (segments.length > 1) {
  211. var lastShapeInCollection = shapeData.shape.paths.shapes[shapeData.shape.paths._length - 1];
  212. if (lastShapeInCollection.c) {
  213. var lastShape = newShapesData.pop();
  214. this.addPaths(newShapesData, localShapeCollection);
  215. newShapesData = this.addShapes(shapeData, segments[1], lastShape);
  216. } else {
  217. this.addPaths(newShapesData, localShapeCollection);
  218. newShapesData = this.addShapes(shapeData, segments[1]);
  219. }
  220. }
  221. this.addPaths(newShapesData, localShapeCollection);
  222. }
  223. }
  224. shapeData.shape.paths = localShapeCollection;
  225. }
  226. }
  227. } else if (this._mdf) {
  228. for (i = 0; i < len; i += 1) {
  229. // Releasign Trim Cached paths data when no trim applied in case shapes are modified inbetween.
  230. // Don't remove this even if it's losing cached info.
  231. this.shapes[i].pathsData.length = 0;
  232. this.shapes[i].shape._mdf = true;
  233. }
  234. }
  235. };
  236. TrimModifier.prototype.addPaths = function (newPaths, localShapeCollection) {
  237. var i;
  238. var len = newPaths.length;
  239. for (i = 0; i < len; i += 1) {
  240. localShapeCollection.addShape(newPaths[i]);
  241. }
  242. };
  243. TrimModifier.prototype.addSegment = function (pt1, pt2, pt3, pt4, shapePath, pos, newShape) {
  244. shapePath.setXYAt(pt2[0], pt2[1], 'o', pos);
  245. shapePath.setXYAt(pt3[0], pt3[1], 'i', pos + 1);
  246. if (newShape) {
  247. shapePath.setXYAt(pt1[0], pt1[1], 'v', pos);
  248. }
  249. shapePath.setXYAt(pt4[0], pt4[1], 'v', pos + 1);
  250. };
  251. TrimModifier.prototype.addSegmentFromArray = function (points, shapePath, pos, newShape) {
  252. shapePath.setXYAt(points[1], points[5], 'o', pos);
  253. shapePath.setXYAt(points[2], points[6], 'i', pos + 1);
  254. if (newShape) {
  255. shapePath.setXYAt(points[0], points[4], 'v', pos);
  256. }
  257. shapePath.setXYAt(points[3], points[7], 'v', pos + 1);
  258. };
  259. TrimModifier.prototype.addShapes = function (shapeData, shapeSegment, shapePath) {
  260. var pathsData = shapeData.pathsData;
  261. var shapePaths = shapeData.shape.paths.shapes;
  262. var i;
  263. var len = shapeData.shape.paths._length;
  264. var j;
  265. var jLen;
  266. var addedLength = 0;
  267. var currentLengthData;
  268. var segmentCount;
  269. var lengths;
  270. var segment;
  271. var shapes = [];
  272. var initPos;
  273. var newShape = true;
  274. if (!shapePath) {
  275. shapePath = shapePool.newElement();
  276. segmentCount = 0;
  277. initPos = 0;
  278. } else {
  279. segmentCount = shapePath._length;
  280. initPos = shapePath._length;
  281. }
  282. shapes.push(shapePath);
  283. for (i = 0; i < len; i += 1) {
  284. lengths = pathsData[i].lengths;
  285. shapePath.c = shapePaths[i].c;
  286. jLen = shapePaths[i].c ? lengths.length : lengths.length + 1;
  287. for (j = 1; j < jLen; j += 1) {
  288. currentLengthData = lengths[j - 1];
  289. if (addedLength + currentLengthData.addedLength < shapeSegment.s) {
  290. addedLength += currentLengthData.addedLength;
  291. shapePath.c = false;
  292. } else if (addedLength > shapeSegment.e) {
  293. shapePath.c = false;
  294. break;
  295. } else {
  296. if (shapeSegment.s <= addedLength && shapeSegment.e >= addedLength + currentLengthData.addedLength) {
  297. this.addSegment(shapePaths[i].v[j - 1], shapePaths[i].o[j - 1], shapePaths[i].i[j], shapePaths[i].v[j], shapePath, segmentCount, newShape);
  298. newShape = false;
  299. } else {
  300. segment = bez.getNewSegment(shapePaths[i].v[j - 1], shapePaths[i].v[j], shapePaths[i].o[j - 1], shapePaths[i].i[j], (shapeSegment.s - addedLength) / currentLengthData.addedLength, (shapeSegment.e - addedLength) / currentLengthData.addedLength, lengths[j - 1]);
  301. this.addSegmentFromArray(segment, shapePath, segmentCount, newShape);
  302. // this.addSegment(segment.pt1, segment.pt3, segment.pt4, segment.pt2, shapePath, segmentCount, newShape);
  303. newShape = false;
  304. shapePath.c = false;
  305. }
  306. addedLength += currentLengthData.addedLength;
  307. segmentCount += 1;
  308. }
  309. }
  310. if (shapePaths[i].c && lengths.length) {
  311. currentLengthData = lengths[j - 1];
  312. if (addedLength <= shapeSegment.e) {
  313. var segmentLength = lengths[j - 1].addedLength;
  314. if (shapeSegment.s <= addedLength && shapeSegment.e >= addedLength + segmentLength) {
  315. this.addSegment(shapePaths[i].v[j - 1], shapePaths[i].o[j - 1], shapePaths[i].i[0], shapePaths[i].v[0], shapePath, segmentCount, newShape);
  316. newShape = false;
  317. } else {
  318. segment = bez.getNewSegment(shapePaths[i].v[j - 1], shapePaths[i].v[0], shapePaths[i].o[j - 1], shapePaths[i].i[0], (shapeSegment.s - addedLength) / segmentLength, (shapeSegment.e - addedLength) / segmentLength, lengths[j - 1]);
  319. this.addSegmentFromArray(segment, shapePath, segmentCount, newShape);
  320. // this.addSegment(segment.pt1, segment.pt3, segment.pt4, segment.pt2, shapePath, segmentCount, newShape);
  321. newShape = false;
  322. shapePath.c = false;
  323. }
  324. } else {
  325. shapePath.c = false;
  326. }
  327. addedLength += currentLengthData.addedLength;
  328. segmentCount += 1;
  329. }
  330. if (shapePath._length) {
  331. shapePath.setXYAt(shapePath.v[initPos][0], shapePath.v[initPos][1], 'i', initPos);
  332. shapePath.setXYAt(shapePath.v[shapePath._length - 1][0], shapePath.v[shapePath._length - 1][1], 'o', shapePath._length - 1);
  333. }
  334. if (addedLength > shapeSegment.e) {
  335. break;
  336. }
  337. if (i < len - 1) {
  338. shapePath = shapePool.newElement();
  339. newShape = true;
  340. shapes.push(shapePath);
  341. segmentCount = 0;
  342. }
  343. }
  344. return shapes;
  345. };
  346. export default TrimModifier;