DrawClosedCurve.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. import {
  2. createTooltip
  3. } from "../../../common/common.js";
  4. import {
  5. isRuntimeApp,
  6. isRuntimeWeb,
  7. createOperationMainDom,
  8. showTooltipMessage
  9. } from "../../../common/RuntimeEnvironment.js";
  10. // 闭合曲面
  11. class DrawClosedCurve {
  12. constructor(arg) {
  13. this.viewer = arg.viewer;
  14. this.Cesium = arg.Cesium;
  15. this.floatingPoint = null; //标识点
  16. this._ClosedCurve = null; //活动闭合曲面
  17. this._ClosedCurveLast = null; //最后一个闭合曲面
  18. this._positions = []; //活动点
  19. this._entities_point = []; //脏数据
  20. this._entities_ClosedCurve = []; //脏数据
  21. this._ClosedCurveData = null; //用于构造闭合曲面数据
  22. this.ZERO_TOLERANCE = 0.0001;
  23. this.FITTING_COUNT = 100;
  24. this.t = 0.3
  25. this.objId = Number((new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0));
  26. this.DrawStartEvent = new Cesium.Event(); //开始绘制事件
  27. this.DrawEndEvent = new Cesium.Event(); //结束绘制事件
  28. this._tooltip = createTooltip(this.viewer.container);
  29. /* 通用参数集合 */
  30. this._param = {
  31. id: "DrawStraightArrow",
  32. polygonColor: 'rgba(0,255,0,0.5)', //面填充颜色
  33. outlineColor: 'rgba(255, 255, 255, 1)', //边框颜色
  34. outlineWidth: 1, //边框宽度
  35. }
  36. /* 创建面材质 */
  37. this.polygonMaterial = Cesium.Color.fromCssColorString(this._param.polygonColor);
  38. /* 创建线材质 */
  39. // this.outlineMaterial = new Cesium.PolylineDashMaterialProperty({//曲线
  40. // dashLength: 16,
  41. // color: Cesium.Color.fromCssColorString(this._param.outlineColor)
  42. // });
  43. this.outlineMaterial = Cesium.Color.fromCssColorString(this._param.outlineColor);
  44. }
  45. //返回闭合曲面
  46. get ClosedCurve() {
  47. return this._ClosedCurveLast;
  48. }
  49. //返回闭合曲面数据用于加载闭合曲面
  50. getData() {
  51. return this._ClosedCurveData;
  52. }
  53. // 修改编辑调用计算
  54. computePosition(data) {
  55. let $this = this;
  56. if (data.length < 2) return
  57. let pnts = []
  58. for (let p = 0; p < data.length; p++) {
  59. pnts.push($this.cartesianToLatlng(data[p]))
  60. }
  61. this._ClosedCurveData = Array.from(pnts);
  62. pnts.push(pnts[0], pnts[1])
  63. let [normals, pList] = [
  64. [],
  65. []
  66. ]
  67. for (let i = 0; i < pnts.length - 2; i++) {
  68. let normalPoints = $this.getBisectorNormals($this.t, pnts[i], pnts[i + 1], pnts[i + 2])
  69. normals = normals.concat(normalPoints)
  70. }
  71. let count = normals.length
  72. normals = [normals[count - 1]].concat(normals.slice(0, count - 1))
  73. for (let i = 0; i < pnts.length - 2; i++) {
  74. let pnt1 = pnts[i]
  75. let pnt2 = pnts[i + 1]
  76. pList.push($this.LatlngTocartesian(pnt1))
  77. for (let t = 0; t <= $this.FITTING_COUNT; t++) {
  78. let pnt = $this.getCubicValue(t / $this.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2)
  79. pList.push($this.LatlngTocartesian(pnt))
  80. }
  81. pList.push($this.LatlngTocartesian(pnt2))
  82. }
  83. let PolygonHierarchy = new $this.Cesium.PolygonHierarchy(pList);
  84. return {
  85. PolygonHierarchy,
  86. pList
  87. }
  88. }
  89. //加载闭合曲面
  90. addload(data) {
  91. let $this = this
  92. if (data.length < 2) return
  93. let pnts = Array.from(data);
  94. pnts.push(pnts[0], pnts[1])
  95. let [normals, pList] = [
  96. [],
  97. []
  98. ]
  99. for (let i = 0; i < pnts.length - 2; i++) {
  100. let normalPoints = $this.getBisectorNormals($this.t, pnts[i], pnts[i + 1], pnts[i + 2])
  101. normals = normals.concat(normalPoints)
  102. }
  103. let count = normals.length
  104. normals = [normals[count - 1]].concat(normals.slice(0, count - 1))
  105. for (let i = 0; i < pnts.length - 2; i++) {
  106. let pnt1 = pnts[i]
  107. let pnt2 = pnts[i + 1]
  108. pList.push($this.LatlngTocartesian(pnt1))
  109. for (let t = 0; t <= $this.FITTING_COUNT; t++) {
  110. let pnt = $this.getCubicValue(t / $this.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2)
  111. pList.push($this.LatlngTocartesian(pnt))
  112. }
  113. pList.push($this.LatlngTocartesian(pnt2))
  114. }
  115. console.log(data, pnts)
  116. var arrowEntity = $this.viewer.entities.add({
  117. Type: 'DrawClosedCurve',
  118. Position: data,
  119. id: data.id || $this.objId,
  120. polygon: {
  121. hierarchy: new $this.Cesium.PolygonHierarchy(pList),
  122. show: true,
  123. fill: true,
  124. clampToGround: true,
  125. material: $this.polygonMaterial
  126. },
  127. polyline: {
  128. positions: pList,
  129. show: true,
  130. material: new Cesium.PolylineDashMaterialProperty({
  131. color: Cesium.Color.YELLOW,
  132. }),
  133. width: 3,
  134. clampToGround: true
  135. }
  136. });
  137. return arrowEntity;
  138. }
  139. //开始创建
  140. startCreate(drawType) {
  141. if (isRuntimeApp()) {
  142. showTooltipMessage("点击开始绘制");
  143. }
  144. var $this = this;
  145. this.drawType = drawType;
  146. this.handler = new this.Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
  147. //单击开始绘制
  148. this.handler.setInputAction(function(evt) {
  149. if (isRuntimeApp()) {
  150. //屏幕坐标转地形上坐标
  151. var cartesian = $this.getCatesian3FromPX(evt.position);
  152. if (!cartesian) {
  153. return;
  154. }
  155. $this.createPoint(cartesian); // 绘制点
  156. $this._positions.push(cartesian);
  157. if ($this._positions.length > 2) {
  158. showTooltipMessage("点击添加点,点击完成按钮,结束绘制");
  159. if ($this._positions.length === 3) {
  160. if (!$this.Cesium.defined($this._ClosedCurve)) {
  161. $this._ClosedCurve = $this.createClosedCurve();
  162. //创建按钮
  163. createOperationMainDom();
  164. //隐藏回退按钮
  165. document.getElementById("btnDrawBackout").style.display = 'none';
  166. //完成绘制
  167. document.getElementById("btnDrawComplete").onclick = () => {
  168. $this._ClosedCurveData = $this._positions.concat();
  169. $this.viewer.entities.remove($this._ClosedCurve); //移除
  170. $this._ClosedCurve = null;
  171. $this._positions = [];
  172. var lnglatArr = [];
  173. for (var i = 0; i < $this._ClosedCurveData.length; i++) {
  174. var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]);
  175. lnglatArr.push(lnglat)
  176. }
  177. $this._ClosedCurveData = lnglatArr;
  178. var ClosedCurve = $this.addload(lnglatArr); //加载
  179. $this._entities_ClosedCurve.push(ClosedCurve);
  180. $this._ClosedCurveLast = ClosedCurve;
  181. $this.clearPoint();
  182. $this.destroy();
  183. let buttonDiv = document.getElementById("drawButtonDiv");
  184. if (buttonDiv) {
  185. //从页面移除
  186. document.body.removeChild(buttonDiv);
  187. }
  188. }
  189. }
  190. }
  191. } else {
  192. showTooltipMessage("点击添加点");
  193. }
  194. } else {
  195. console.log('监听鼠标事件', '单击')
  196. /* 锁定点击事件 以免和双击事件冲突 */
  197. clearTimeout($this._timer);
  198. $this._timer = setTimeout(function() {
  199. //屏幕坐标转地形上坐标
  200. var cartesian = $this.getCatesian3FromPX(evt.position);
  201. if ($this._positions.length == 0) {
  202. $this.floatingPoint = $this.createPoint(cartesian);
  203. }
  204. $this._positions.push(cartesian);
  205. $this.createPoint(cartesian);
  206. }, 200);
  207. }
  208. }, $this.Cesium.ScreenSpaceEventType.LEFT_CLICK);
  209. //移动时绘制面
  210. this.handler.setInputAction(function(evt) {
  211. /* 如果运行环境是App 则禁止使用鼠标移动事件 */
  212. if (isRuntimeApp()) return;
  213. if ($this._positions.length == 0) {
  214. $this._tooltip.showAt(evt.endPosition, "点击开始绘制");
  215. } else {
  216. $this._tooltip.showAt(evt.endPosition, "点击添加点");
  217. }
  218. if ($this._positions.length < 2) return;
  219. $this._tooltip.showAt(evt.endPosition, "点击添加点,双击结束绘制");
  220. var cartesian = $this.getCatesian3FromPX(evt.endPosition);
  221. if ($this._positions.length == 2) {
  222. $this._positions.push(cartesian);
  223. }
  224. $this._positions.pop();
  225. $this._positions.push(cartesian);
  226. if (!$this.Cesium.defined($this._ClosedCurve)) {
  227. $this._ClosedCurve = $this.createClosedCurve();
  228. }
  229. }, $this.Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  230. //右击停止采集改为双击结束
  231. this.handler.setInputAction(function(evt) {
  232. // if (!$this._ClosedCurve) return;
  233. // var cartesian = $this.getCatesian3FromPX(evt.position);
  234. // $this._positions.pop();
  235. // $this._positions.push(cartesian);
  236. // $this._ClosedCurveData = $this._positions.concat();
  237. // $this.viewer.entities.remove($this._ClosedCurve); //移除
  238. // $this._ClosedCurve = null;
  239. // $this._positions = [];
  240. // $this.floatingPoint.position.setValue(cartesian);
  241. // var lnglatArr = [];
  242. // for (var i = 0; i < $this._ClosedCurveData.length; i++) {
  243. // var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]);
  244. // lnglatArr.push(lnglat)
  245. // }
  246. // $this._ClosedCurveData = lnglatArr;
  247. // var ClosedCurve = $this.addload(lnglatArr); //加载
  248. // $this._entities_ClosedCurve.push(ClosedCurve);
  249. // $this._ClosedCurveLast = ClosedCurve;
  250. // $this.clearPoint();
  251. // $this.destroy()
  252. }, $this.Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  253. //双击结束
  254. this.handler.setInputAction(function(evt) {
  255. /* 如果运行环境是App 则禁止使用鼠标双击事件 */
  256. if (isRuntimeApp()) return;
  257. console.log('监听鼠标事件', '双击')
  258. /* 解除锁定 */
  259. clearTimeout($this._timer);
  260. if (!$this._ClosedCurve) return;
  261. var cartesian = $this.getCatesian3FromPX(evt.position);
  262. $this._positions.pop();
  263. $this._positions.push(cartesian);
  264. $this._ClosedCurveData = $this._positions.concat();
  265. $this.viewer.entities.remove($this._ClosedCurve); //移除
  266. $this._ClosedCurve = null;
  267. $this._positions = [];
  268. $this.floatingPoint.position.setValue(cartesian);
  269. var lnglatArr = [];
  270. for (var i = 0; i < $this._ClosedCurveData.length; i++) {
  271. var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]);
  272. lnglatArr.push(lnglat)
  273. }
  274. $this._ClosedCurveData = lnglatArr;
  275. var ClosedCurve = $this.addload(lnglatArr); //加载
  276. $this._entities_ClosedCurve.push(ClosedCurve);
  277. $this._ClosedCurveLast = ClosedCurve;
  278. $this.clearPoint();
  279. $this.destroy();
  280. $this._tooltip.setVisible(false);
  281. }, $this.Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
  282. }
  283. //创建直线闭合曲面
  284. createClosedCurve() {
  285. // console.log(this._positions)
  286. var $this = this;
  287. $this.pLists = ''
  288. var arrowEntity = $this.viewer.entities.add({
  289. polygon: {
  290. hierarchy: new $this.Cesium.CallbackProperty(
  291. function() {
  292. if ($this._positions.length < 2) return
  293. let pnts = []
  294. for (let p = 0; p < $this._positions.length; p++) {
  295. pnts.push($this.cartesianToLatlng($this._positions[p]))
  296. }
  297. pnts.push(pnts[0], pnts[1])
  298. let [normals, pList] = [
  299. [],
  300. []
  301. ]
  302. for (let i = 0; i < pnts.length - 2; i++) {
  303. let normalPoints = $this.getBisectorNormals($this.t, pnts[i], pnts[i + 1], pnts[i + 2])
  304. normals = normals.concat(normalPoints)
  305. }
  306. let count = normals.length
  307. normals = [normals[count - 1]].concat(normals.slice(0, count - 1))
  308. for (let i = 0; i < pnts.length - 2; i++) {
  309. let pnt1 = pnts[i]
  310. let pnt2 = pnts[i + 1]
  311. pList.push($this.LatlngTocartesian(pnt1))
  312. for (let t = 0; t <= $this.FITTING_COUNT; t++) {
  313. let pnt = $this.getCubicValue(t / $this.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2)
  314. pList.push($this.LatlngTocartesian(pnt))
  315. }
  316. pList.push($this.LatlngTocartesian(pnt2))
  317. }
  318. $this.pLists = pList
  319. return new $this.Cesium.PolygonHierarchy(pList);
  320. }, false),
  321. show: true,
  322. fill: true,
  323. clampToGround: true,
  324. material: $this.polygonMaterial
  325. },
  326. polyline: {
  327. positions: new $this.Cesium.CallbackProperty(
  328. function() {
  329. return $this.pLists
  330. }, false
  331. ),
  332. show: true,
  333. material: new Cesium.PolylineDashMaterialProperty({
  334. color: Cesium.Color.YELLOW,
  335. }),
  336. width: 3,
  337. clampToGround: true
  338. }
  339. })
  340. $this._entities_ClosedCurve.push(arrowEntity);
  341. return arrowEntity
  342. }
  343. //创建点
  344. createPoint(cartesian) {
  345. var $this = this;
  346. var point = this.viewer.entities.add({
  347. position: cartesian,
  348. point: {
  349. pixelSize: 10,
  350. color: $this.Cesium.Color.RED,
  351. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  352. }
  353. });
  354. $this._entities_point.push(point);
  355. return point;
  356. }
  357. /**
  358. * 世界坐标转经纬度
  359. */
  360. cartesianToLatlng(cartesian) {
  361. var latlng = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
  362. var lat = this.Cesium.Math.toDegrees(latlng.latitude);
  363. var lng = this.Cesium.Math.toDegrees(latlng.longitude);
  364. return [lng, lat];
  365. }
  366. /**
  367. * 经纬度转世界坐标
  368. */
  369. LatlngTocartesian(latlng) {
  370. let cartesian3 = this.Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1]);
  371. return cartesian3
  372. }
  373. /**
  374. * 经纬度坐标转墨卡托坐标
  375. */
  376. // 墨卡托坐标系:展开地球,赤道作为x轴,向东为x轴正方,本初子午线作为y轴,向北为y轴正方向。
  377. // 数字20037508.34是地球赤道周长的一半:地球半径6378137米,赤道周长2*PI*r = 2 * 20037508.3427892,墨卡托坐标x轴区间[-20037508.3427892,20037508.3427892]
  378. lonLatToMercator(Latlng) {
  379. var E = Latlng[0];
  380. var N = Latlng[1];
  381. var x = E * 20037508.34 / 180;
  382. var y = Math.log(Math.tan((90 + N) * Math.PI / 360)) / (Math.PI / 180);
  383. y = y * 20037508.34 / 180;
  384. return [x, y]
  385. }
  386. /**
  387. * 墨卡托坐标转经纬度坐标转
  388. */
  389. WebMercator2lonLat(mercator) {
  390. let x = mercator[0] / 20037508.34 * 180;
  391. let ly = mercator[1] / 20037508.34 * 180;
  392. let y = 180 / Math.PI * (2 * Math.atan(Math.exp(ly * Math.PI / 180)) - Math.PI / 2)
  393. return [x, y];
  394. }
  395. //销毁
  396. destroy() {
  397. if (this.handler) {
  398. this.handler.destroy();
  399. this.handler = null;
  400. }
  401. }
  402. clearPoint() {
  403. this.DrawEndEvent.raiseEvent(this._ClosedCurveLast, this._ClosedCurveData);
  404. for (var i = 0; i < this._entities_point.length; i++) {
  405. this.viewer.entities.remove(this._entities_point[i]);
  406. }
  407. this._entities_point = []; //脏数据
  408. }
  409. //清空实体对象
  410. clear() {
  411. for (var i = 0; i < this._entities_point.length; i++) {
  412. this.viewer.entities.remove(this._entities_point[i]);
  413. }
  414. for (var i = 0; i < this._entities_ClosedCurve.length; i++) {
  415. this.viewer.entities.remove(this._entities_ClosedCurve[i]);
  416. }
  417. this.floatingPoint = null; //标识点
  418. this._ClosedCurve = null; //活动闭合曲面
  419. this._ClosedCurveLast = null; //最后一个闭合曲面
  420. this._positions = []; //活动点
  421. this._entities_point = []; //脏数据
  422. this._entities_ClosedCurve = []; //脏数据
  423. this._ClosedCurveData = null; //用于构造闭合曲面数据
  424. }
  425. getCatesian3FromPX(px) {
  426. var cartesian;
  427. var ray = this.viewer.camera.getPickRay(px);
  428. if (!ray) return null;
  429. cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene);
  430. return cartesian;
  431. }
  432. // 求取闭合曲面坐标函数/
  433. //闭合曲面配置函数
  434. /**
  435. * getBisectorNormals
  436. * @param t
  437. * @param pnt1
  438. * @param pnt2
  439. * @param pnt3
  440. * @returns {[*,*]}
  441. */
  442. getBisectorNormals(t, pnt1, pnt2, pnt3) {
  443. let $this = this
  444. let normal = $this.getNormal(pnt1, pnt2, pnt3)
  445. let [bisectorNormalRight, bisectorNormalLeft, dt, x, y] = [null, null, null, null, null]
  446. let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
  447. let uX = normal[0] / dist
  448. let uY = normal[1] / dist
  449. let d1 = $this.MathDistance(pnt1, pnt2)
  450. let d2 = $this.MathDistance(pnt2, pnt3)
  451. if (dist > $this.ZERO_TOLERANCE) {
  452. if ($this.isClockWise(pnt1, pnt2, pnt3)) {
  453. dt = t * d1
  454. x = pnt2[0] - dt * uY
  455. y = pnt2[1] + dt * uX
  456. bisectorNormalRight = [x, y]
  457. dt = t * d2
  458. x = pnt2[0] + dt * uY
  459. y = pnt2[1] - dt * uX
  460. bisectorNormalLeft = [x, y]
  461. } else {
  462. dt = t * d1
  463. x = pnt2[0] + dt * uY
  464. y = pnt2[1] - dt * uX
  465. bisectorNormalRight = [x, y]
  466. dt = t * d2
  467. x = pnt2[0] - dt * uY
  468. y = pnt2[1] + dt * uX
  469. bisectorNormalLeft = [x, y]
  470. }
  471. } else {
  472. x = pnt2[0] + t * (pnt1[0] - pnt2[0])
  473. y = pnt2[1] + t * (pnt1[1] - pnt2[1])
  474. bisectorNormalRight = [x, y]
  475. x = pnt2[0] + t * (pnt3[0] - pnt2[0])
  476. y = pnt2[1] + t * (pnt3[1] - pnt2[1])
  477. bisectorNormalLeft = [x, y]
  478. }
  479. return [bisectorNormalRight, bisectorNormalLeft]
  480. }
  481. /**
  482. * 获取默认三点的内切圆
  483. * @param pnt1
  484. * @param pnt2
  485. * @param pnt3
  486. * @returns {[*,*]}
  487. */
  488. getNormal(pnt1, pnt2, pnt3) {
  489. let dX1 = pnt1[0] - pnt2[0]
  490. let dY1 = pnt1[1] - pnt2[1]
  491. let d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1)
  492. dX1 /= d1
  493. dY1 /= d1
  494. let dX2 = pnt3[0] - pnt2[0]
  495. let dY2 = pnt3[1] - pnt2[1]
  496. let d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2)
  497. dX2 /= d2
  498. dY2 /= d2
  499. let uX = dX1 + dX2
  500. let uY = dY1 + dY2
  501. return [uX, uY]
  502. }
  503. /**
  504. * 计算两个坐标之间的距离
  505. * @ignore
  506. * @param pnt1
  507. * @param pnt2
  508. * @returns {number}
  509. * @constructor
  510. */
  511. MathDistance(pnt1, pnt2) {
  512. return (Math.sqrt(Math.pow((pnt1[0] - pnt2[0]), 2) + Math.pow((pnt1[1] - pnt2[1]), 2)))
  513. }
  514. /**
  515. * 判断是否是顺时针
  516. * @param pnt1
  517. * @param pnt2
  518. * @param pnt3
  519. * @returns {boolean}
  520. */
  521. isClockWise(pnt1, pnt2, pnt3) {
  522. return ((pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) > (pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0]))
  523. }
  524. /**
  525. * 获取立方值
  526. * @param t
  527. * @param startPnt
  528. * @param cPnt1
  529. * @param cPnt2
  530. * @param endPnt
  531. * @returns {[*,*]}
  532. */
  533. getCubicValue(t, startPnt, cPnt1, cPnt2, endPnt) {
  534. t = Math.max(Math.min(t, 1), 0)
  535. let [tp, t2] = [(1 - t), (t * t)]
  536. let t3 = t2 * t
  537. let tp2 = tp * tp
  538. let tp3 = tp2 * tp
  539. let x = (tp3 * startPnt[0]) + (3 * tp2 * t * cPnt1[0]) + (3 * tp * t2 * cPnt2[0]) + (t3 * endPnt[0])
  540. let y = (tp3 * startPnt[1]) + (3 * tp2 * t * cPnt1[1]) + (3 * tp * t2 * cPnt2[1]) + (t3 * endPnt[1])
  541. return [x, y]
  542. }
  543. }
  544. export default DrawClosedCurve