DrawClosedCurve.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  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("btnDrawComplete").onclick = () => {
  166. $this._ClosedCurveData = $this._positions.concat();
  167. $this.viewer.entities.remove($this._ClosedCurve); //移除
  168. $this._ClosedCurve = null;
  169. $this._positions = [];
  170. var lnglatArr = [];
  171. for (var i = 0; i < $this._ClosedCurveData.length; i++) {
  172. var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]);
  173. lnglatArr.push(lnglat)
  174. }
  175. $this._ClosedCurveData = lnglatArr;
  176. var ClosedCurve = $this.addload(lnglatArr); //加载
  177. $this._entities_ClosedCurve.push(ClosedCurve);
  178. $this._ClosedCurveLast = ClosedCurve;
  179. $this.clearPoint();
  180. $this.destroy();
  181. let buttonDiv = document.getElementById("drawButtonDiv");
  182. if (buttonDiv) {
  183. //从页面移除
  184. document.body.removeChild(buttonDiv);
  185. }
  186. }
  187. }
  188. }
  189. } else {
  190. showTooltipMessage("点击添加点");
  191. }
  192. } else {
  193. console.log('监听鼠标事件', '单击')
  194. /* 锁定点击事件 以免和双击事件冲突 */
  195. clearTimeout($this._timer);
  196. $this._timer = setTimeout(function() {
  197. //屏幕坐标转地形上坐标
  198. var cartesian = $this.getCatesian3FromPX(evt.position);
  199. if ($this._positions.length == 0) {
  200. $this.floatingPoint = $this.createPoint(cartesian);
  201. }
  202. $this._positions.push(cartesian);
  203. $this.createPoint(cartesian);
  204. }, 200);
  205. }
  206. }, $this.Cesium.ScreenSpaceEventType.LEFT_CLICK);
  207. //移动时绘制面
  208. this.handler.setInputAction(function(evt) {
  209. /* 如果运行环境是App 则禁止使用鼠标移动事件 */
  210. if (isRuntimeApp()) return;
  211. if ($this._positions.length == 0) {
  212. $this._tooltip.showAt(evt.endPosition, "点击开始绘制");
  213. } else {
  214. $this._tooltip.showAt(evt.endPosition, "点击添加点");
  215. }
  216. if ($this._positions.length < 2) return;
  217. $this._tooltip.showAt(evt.endPosition, "点击添加点,双击结束绘制");
  218. var cartesian = $this.getCatesian3FromPX(evt.endPosition);
  219. if ($this._positions.length == 2) {
  220. $this._positions.push(cartesian);
  221. }
  222. $this._positions.pop();
  223. $this._positions.push(cartesian);
  224. if (!$this.Cesium.defined($this._ClosedCurve)) {
  225. $this._ClosedCurve = $this.createClosedCurve();
  226. }
  227. }, $this.Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  228. //右击停止采集改为双击结束
  229. this.handler.setInputAction(function(evt) {
  230. // if (!$this._ClosedCurve) return;
  231. // var cartesian = $this.getCatesian3FromPX(evt.position);
  232. // $this._positions.pop();
  233. // $this._positions.push(cartesian);
  234. // $this._ClosedCurveData = $this._positions.concat();
  235. // $this.viewer.entities.remove($this._ClosedCurve); //移除
  236. // $this._ClosedCurve = null;
  237. // $this._positions = [];
  238. // $this.floatingPoint.position.setValue(cartesian);
  239. // var lnglatArr = [];
  240. // for (var i = 0; i < $this._ClosedCurveData.length; i++) {
  241. // var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]);
  242. // lnglatArr.push(lnglat)
  243. // }
  244. // $this._ClosedCurveData = lnglatArr;
  245. // var ClosedCurve = $this.addload(lnglatArr); //加载
  246. // $this._entities_ClosedCurve.push(ClosedCurve);
  247. // $this._ClosedCurveLast = ClosedCurve;
  248. // $this.clearPoint();
  249. // $this.destroy()
  250. }, $this.Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  251. //双击结束
  252. this.handler.setInputAction(function(evt) {
  253. /* 如果运行环境是App 则禁止使用鼠标双击事件 */
  254. if (isRuntimeApp()) return;
  255. console.log('监听鼠标事件', '双击')
  256. /* 解除锁定 */
  257. clearTimeout($this._timer);
  258. if (!$this._ClosedCurve) return;
  259. var cartesian = $this.getCatesian3FromPX(evt.position);
  260. $this._positions.pop();
  261. $this._positions.push(cartesian);
  262. $this._ClosedCurveData = $this._positions.concat();
  263. $this.viewer.entities.remove($this._ClosedCurve); //移除
  264. $this._ClosedCurve = null;
  265. $this._positions = [];
  266. $this.floatingPoint.position.setValue(cartesian);
  267. var lnglatArr = [];
  268. for (var i = 0; i < $this._ClosedCurveData.length; i++) {
  269. var lnglat = $this.cartesianToLatlng($this._ClosedCurveData[i]);
  270. lnglatArr.push(lnglat)
  271. }
  272. $this._ClosedCurveData = lnglatArr;
  273. var ClosedCurve = $this.addload(lnglatArr); //加载
  274. $this._entities_ClosedCurve.push(ClosedCurve);
  275. $this._ClosedCurveLast = ClosedCurve;
  276. $this.clearPoint();
  277. $this.destroy();
  278. $this._tooltip.setVisible(false);
  279. }, $this.Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
  280. }
  281. //创建直线闭合曲面
  282. createClosedCurve() {
  283. // console.log(this._positions)
  284. var $this = this;
  285. $this.pLists = ''
  286. var arrowEntity = $this.viewer.entities.add({
  287. polygon: {
  288. hierarchy: new $this.Cesium.CallbackProperty(
  289. function() {
  290. if ($this._positions.length < 2) return
  291. let pnts = []
  292. for (let p = 0; p < $this._positions.length; p++) {
  293. pnts.push($this.cartesianToLatlng($this._positions[p]))
  294. }
  295. pnts.push(pnts[0], pnts[1])
  296. let [normals, pList] = [
  297. [],
  298. []
  299. ]
  300. for (let i = 0; i < pnts.length - 2; i++) {
  301. let normalPoints = $this.getBisectorNormals($this.t, pnts[i], pnts[i + 1], pnts[i + 2])
  302. normals = normals.concat(normalPoints)
  303. }
  304. let count = normals.length
  305. normals = [normals[count - 1]].concat(normals.slice(0, count - 1))
  306. for (let i = 0; i < pnts.length - 2; i++) {
  307. let pnt1 = pnts[i]
  308. let pnt2 = pnts[i + 1]
  309. pList.push($this.LatlngTocartesian(pnt1))
  310. for (let t = 0; t <= $this.FITTING_COUNT; t++) {
  311. let pnt = $this.getCubicValue(t / $this.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2)
  312. pList.push($this.LatlngTocartesian(pnt))
  313. }
  314. pList.push($this.LatlngTocartesian(pnt2))
  315. }
  316. $this.pLists = pList
  317. return new $this.Cesium.PolygonHierarchy(pList);
  318. }, false),
  319. show: true,
  320. fill: true,
  321. clampToGround: true,
  322. material: $this.polygonMaterial
  323. },
  324. polyline: {
  325. positions: new $this.Cesium.CallbackProperty(
  326. function() {
  327. return $this.pLists
  328. }, false
  329. ),
  330. show: true,
  331. material: new Cesium.PolylineDashMaterialProperty({
  332. color: Cesium.Color.YELLOW,
  333. }),
  334. width: 3,
  335. clampToGround: true
  336. }
  337. })
  338. $this._entities_ClosedCurve.push(arrowEntity);
  339. return arrowEntity
  340. }
  341. //创建点
  342. createPoint(cartesian) {
  343. var $this = this;
  344. var point = this.viewer.entities.add({
  345. position: cartesian,
  346. point: {
  347. pixelSize: 10,
  348. color: $this.Cesium.Color.RED,
  349. heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
  350. }
  351. });
  352. $this._entities_point.push(point);
  353. return point;
  354. }
  355. /**
  356. * 世界坐标转经纬度
  357. */
  358. cartesianToLatlng(cartesian) {
  359. var latlng = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
  360. var lat = this.Cesium.Math.toDegrees(latlng.latitude);
  361. var lng = this.Cesium.Math.toDegrees(latlng.longitude);
  362. return [lng, lat];
  363. }
  364. /**
  365. * 经纬度转世界坐标
  366. */
  367. LatlngTocartesian(latlng) {
  368. let cartesian3 = this.Cesium.Cartesian3.fromDegrees(latlng[0], latlng[1]);
  369. return cartesian3
  370. }
  371. /**
  372. * 经纬度坐标转墨卡托坐标
  373. */
  374. // 墨卡托坐标系:展开地球,赤道作为x轴,向东为x轴正方,本初子午线作为y轴,向北为y轴正方向。
  375. // 数字20037508.34是地球赤道周长的一半:地球半径6378137米,赤道周长2*PI*r = 2 * 20037508.3427892,墨卡托坐标x轴区间[-20037508.3427892,20037508.3427892]
  376. lonLatToMercator(Latlng) {
  377. var E = Latlng[0];
  378. var N = Latlng[1];
  379. var x = E * 20037508.34 / 180;
  380. var y = Math.log(Math.tan((90 + N) * Math.PI / 360)) / (Math.PI / 180);
  381. y = y * 20037508.34 / 180;
  382. return [x, y]
  383. }
  384. /**
  385. * 墨卡托坐标转经纬度坐标转
  386. */
  387. WebMercator2lonLat(mercator) {
  388. let x = mercator[0] / 20037508.34 * 180;
  389. let ly = mercator[1] / 20037508.34 * 180;
  390. let y = 180 / Math.PI * (2 * Math.atan(Math.exp(ly * Math.PI / 180)) - Math.PI / 2)
  391. return [x, y];
  392. }
  393. //销毁
  394. destroy() {
  395. if (this.handler) {
  396. this.handler.destroy();
  397. this.handler = null;
  398. }
  399. }
  400. clearPoint() {
  401. this.DrawEndEvent.raiseEvent(this._ClosedCurveLast, this._ClosedCurveData);
  402. for (var i = 0; i < this._entities_point.length; i++) {
  403. this.viewer.entities.remove(this._entities_point[i]);
  404. }
  405. this._entities_point = []; //脏数据
  406. }
  407. //清空实体对象
  408. clear() {
  409. for (var i = 0; i < this._entities_point.length; i++) {
  410. this.viewer.entities.remove(this._entities_point[i]);
  411. }
  412. for (var i = 0; i < this._entities_ClosedCurve.length; i++) {
  413. this.viewer.entities.remove(this._entities_ClosedCurve[i]);
  414. }
  415. this.floatingPoint = null; //标识点
  416. this._ClosedCurve = null; //活动闭合曲面
  417. this._ClosedCurveLast = null; //最后一个闭合曲面
  418. this._positions = []; //活动点
  419. this._entities_point = []; //脏数据
  420. this._entities_ClosedCurve = []; //脏数据
  421. this._ClosedCurveData = null; //用于构造闭合曲面数据
  422. }
  423. getCatesian3FromPX(px) {
  424. var cartesian;
  425. var ray = this.viewer.camera.getPickRay(px);
  426. if (!ray) return null;
  427. cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene);
  428. return cartesian;
  429. }
  430. // 求取闭合曲面坐标函数/
  431. //闭合曲面配置函数
  432. /**
  433. * getBisectorNormals
  434. * @param t
  435. * @param pnt1
  436. * @param pnt2
  437. * @param pnt3
  438. * @returns {[*,*]}
  439. */
  440. getBisectorNormals(t, pnt1, pnt2, pnt3) {
  441. let $this = this
  442. let normal = $this.getNormal(pnt1, pnt2, pnt3)
  443. let [bisectorNormalRight, bisectorNormalLeft, dt, x, y] = [null, null, null, null, null]
  444. let dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
  445. let uX = normal[0] / dist
  446. let uY = normal[1] / dist
  447. let d1 = $this.MathDistance(pnt1, pnt2)
  448. let d2 = $this.MathDistance(pnt2, pnt3)
  449. if (dist > $this.ZERO_TOLERANCE) {
  450. if ($this.isClockWise(pnt1, pnt2, pnt3)) {
  451. dt = t * d1
  452. x = pnt2[0] - dt * uY
  453. y = pnt2[1] + dt * uX
  454. bisectorNormalRight = [x, y]
  455. dt = t * d2
  456. x = pnt2[0] + dt * uY
  457. y = pnt2[1] - dt * uX
  458. bisectorNormalLeft = [x, y]
  459. } else {
  460. dt = t * d1
  461. x = pnt2[0] + dt * uY
  462. y = pnt2[1] - dt * uX
  463. bisectorNormalRight = [x, y]
  464. dt = t * d2
  465. x = pnt2[0] - dt * uY
  466. y = pnt2[1] + dt * uX
  467. bisectorNormalLeft = [x, y]
  468. }
  469. } else {
  470. x = pnt2[0] + t * (pnt1[0] - pnt2[0])
  471. y = pnt2[1] + t * (pnt1[1] - pnt2[1])
  472. bisectorNormalRight = [x, y]
  473. x = pnt2[0] + t * (pnt3[0] - pnt2[0])
  474. y = pnt2[1] + t * (pnt3[1] - pnt2[1])
  475. bisectorNormalLeft = [x, y]
  476. }
  477. return [bisectorNormalRight, bisectorNormalLeft]
  478. }
  479. /**
  480. * 获取默认三点的内切圆
  481. * @param pnt1
  482. * @param pnt2
  483. * @param pnt3
  484. * @returns {[*,*]}
  485. */
  486. getNormal(pnt1, pnt2, pnt3) {
  487. let dX1 = pnt1[0] - pnt2[0]
  488. let dY1 = pnt1[1] - pnt2[1]
  489. let d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1)
  490. dX1 /= d1
  491. dY1 /= d1
  492. let dX2 = pnt3[0] - pnt2[0]
  493. let dY2 = pnt3[1] - pnt2[1]
  494. let d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2)
  495. dX2 /= d2
  496. dY2 /= d2
  497. let uX = dX1 + dX2
  498. let uY = dY1 + dY2
  499. return [uX, uY]
  500. }
  501. /**
  502. * 计算两个坐标之间的距离
  503. * @ignore
  504. * @param pnt1
  505. * @param pnt2
  506. * @returns {number}
  507. * @constructor
  508. */
  509. MathDistance(pnt1, pnt2) {
  510. return (Math.sqrt(Math.pow((pnt1[0] - pnt2[0]), 2) + Math.pow((pnt1[1] - pnt2[1]), 2)))
  511. }
  512. /**
  513. * 判断是否是顺时针
  514. * @param pnt1
  515. * @param pnt2
  516. * @param pnt3
  517. * @returns {boolean}
  518. */
  519. isClockWise(pnt1, pnt2, pnt3) {
  520. return ((pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) > (pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0]))
  521. }
  522. /**
  523. * 获取立方值
  524. * @param t
  525. * @param startPnt
  526. * @param cPnt1
  527. * @param cPnt2
  528. * @param endPnt
  529. * @returns {[*,*]}
  530. */
  531. getCubicValue(t, startPnt, cPnt1, cPnt2, endPnt) {
  532. t = Math.max(Math.min(t, 1), 0)
  533. let [tp, t2] = [(1 - t), (t * t)]
  534. let t3 = t2 * t
  535. let tp2 = tp * tp
  536. let tp3 = tp2 * tp
  537. let x = (tp3 * startPnt[0]) + (3 * tp2 * t * cPnt1[0]) + (3 * tp * t2 * cPnt2[0]) + (t3 * endPnt[0])
  538. let y = (tp3 * startPnt[1]) + (3 * tp2 * t * cPnt1[1]) + (3 * tp * t2 * cPnt2[1]) + (t3 * endPnt[1])
  539. return [x, y]
  540. }
  541. }
  542. export default DrawClosedCurve