attach-server.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. import {
  2. SnapResultInfo,
  3. useCustomSnapInfos,
  4. useSnap,
  5. useSnapConfig,
  6. } from "@/core/hook/use-snap";
  7. import { defaultStyle, getSnapInfos, LineData, LineDataLine } from ".";
  8. import {
  9. eqPoint,
  10. getVectorLine,
  11. lineCenter,
  12. lineIntersection,
  13. lineLen,
  14. linePointLen,
  15. linePointProjection,
  16. lineVector,
  17. Pos,
  18. vector2IncludedAngle,
  19. verticalVector,
  20. zeroEq,
  21. } from "@/utils/math";
  22. import {
  23. copy,
  24. frameEebounce,
  25. mergeFuns,
  26. onlyId,
  27. rangMod,
  28. } from "@/utils/shared";
  29. import { MathUtils, Vector2 } from "three";
  30. import { generateSnapInfos, getBaseItem } from "../util";
  31. import { ComponentSnapInfo } from "..";
  32. import { useStore } from "@/core/store";
  33. import { computed, onUnmounted, reactive, ref, Ref, watch } from "vue";
  34. import {
  35. useCursor,
  36. usePointerPos,
  37. useRunHook,
  38. useStage,
  39. } from "@/core/hook/use-global-vars";
  40. import { PropertyDescribes } from "@/core/html-mount/propertys";
  41. import { useMode } from "@/core/hook/use-status";
  42. import { useListener } from "@/core/hook/use-event";
  43. import { Mode } from "@/constant/mode";
  44. import { useViewerInvertTransform } from "@/core/hook/use-viewer";
  45. import { clickListener } from "@/utils/event";
  46. import {
  47. genGetLineIconAttach,
  48. getLineIconMat,
  49. LineIconData,
  50. } from "../line-icon";
  51. import { useDrawIngData } from "@/core/hook/use-draw";
  52. import { useComponentDescribes } from "@/core/hook/use-component";
  53. export type NLineDataCtx = {
  54. del: {
  55. points: Record<string, LineData["points"][0]>;
  56. lines: Record<string, LineDataLine>;
  57. };
  58. add: {
  59. points: Record<string, LineData["points"][0]>;
  60. lines: Record<string, LineDataLine>;
  61. };
  62. update: {
  63. points: Record<string, LineData["points"][0]>;
  64. lines: Record<string, LineDataLine>;
  65. };
  66. };
  67. export const getInitCtx = (): NLineDataCtx => ({
  68. del: {
  69. points: {},
  70. lines: {},
  71. },
  72. add: {
  73. points: {},
  74. lines: {},
  75. },
  76. update: {
  77. points: {},
  78. lines: {},
  79. },
  80. });
  81. export const repPointRef = (
  82. data: LineData,
  83. delId: string,
  84. repId: string,
  85. queUpdate = true
  86. ) => {
  87. for (let i = 0; i < data.lines.length; i++) {
  88. const line = data.lines[i];
  89. if (line.a === delId) {
  90. if (queUpdate) {
  91. data.lines[i] = { ...line, a: repId };
  92. } else {
  93. data.lines[i].a = repId;
  94. }
  95. }
  96. if (line.b === delId) {
  97. if (queUpdate) {
  98. data.lines[i] = { ...line, b: repId };
  99. } else {
  100. data.lines[i].b = repId;
  101. }
  102. }
  103. }
  104. return data;
  105. };
  106. export const getLinePoints = (data: LineData, line: LineDataLine) => [
  107. data.points.find((p) => p.id === line.a)!,
  108. data.points.find((p) => p.id === line.b)!,
  109. ];
  110. export const deduplicateLines = (data: LineData) => {
  111. const seen = new Map<string, LineDataLine>();
  112. let isChange = false;
  113. for (const line of data.lines) {
  114. if (line.a === line.b) continue;
  115. // 生成标准化键:确保 (a,b) 和 (b,a) 被视为相同,并且 a === b 时也去重
  116. const key1 = `${line.a},${line.b}`;
  117. const key2 = `${line.b},${line.a}`;
  118. // 检查是否已存在相同键
  119. const existingKey = seen.has(key1) ? key1 : seen.has(key2) ? key2 : null;
  120. if (existingKey) {
  121. // 如果存在重复键,覆盖旧值(保留尾部元素)
  122. seen.delete(existingKey);
  123. seen.set(key1, line); // 统一存储为 key1 格式
  124. isChange = true;
  125. } else {
  126. // 新记录,直接存储
  127. seen.set(key1, line);
  128. }
  129. }
  130. if (isChange) {
  131. data.lines = Array.from(seen.values());
  132. }
  133. return data;
  134. };
  135. export const getJoinLine = (
  136. data: LineData,
  137. line: LineDataLine,
  138. pId: string
  139. ) => {
  140. const pointIds = [line.a, line.b];
  141. return data.lines
  142. .filter(
  143. (item) =>
  144. (item.a === pId || item.b === pId) &&
  145. !(pointIds.includes(item.a) && pointIds.includes(item.b))
  146. )
  147. .map((line) => {
  148. const pointIds = pId === line.a ? [line.a, line.b] : [line.b, line.a];
  149. return {
  150. ...line,
  151. points: pointIds.map((id) => data.points.find((p) => p.id === id)!),
  152. };
  153. });
  154. };
  155. export const foreNormalLineData = (data:LineData) => {
  156. for (let i = 0; i < data.lines.length; i++) {
  157. const {a, b} = data.lines[i]
  158. if (!data.points.some(p => p.id === a) || !data.points.some(p => p.id === b)) {
  159. data.lines.splice(i--, 1)
  160. }
  161. }
  162. for (let i = 0; i < data.points.length; i++) {
  163. const id = data.points[i].id
  164. if (!data.lines.some(l => l.a === id || l.b === id)) {
  165. data.points.splice(i, 1)
  166. }
  167. }
  168. }
  169. export const normalLineData = (data: LineData, ctx: NLineDataCtx) => {
  170. const changePoints = [
  171. ...Object.values(ctx.add.points),
  172. ...Object.values(ctx.update.points),
  173. ];
  174. // 合并相同点
  175. for (const p2 of changePoints) {
  176. const ndx = data.points.findIndex((item) => item.id === p2.id);
  177. if (!~ndx) continue;
  178. for (let i = 0; i < data.points.length; i++) {
  179. const p1 = data.points[i];
  180. if (p1.id !== p2.id && eqPoint(p1, p2)) {
  181. repPointRef(data, p1.id, p2.id);
  182. data.points.splice(i, 1);
  183. i--;
  184. }
  185. }
  186. }
  187. // 删除线a b 点一样的线段
  188. for (let i = 0; i < data.lines.length; i++) {
  189. const line = data.lines[i];
  190. if (line.a === line.b) {
  191. data.lines.splice(i--, 1);
  192. }
  193. }
  194. // 删除游离点
  195. const pointIds = Object.values(ctx.del.lines).flatMap((item) => [
  196. item.a,
  197. item.b,
  198. ]);
  199. pointIds.push(...Object.keys(ctx.add.points));
  200. const linePointIds = data.lines.flatMap((item) => [item.a, item.b]);
  201. for (let id of pointIds) {
  202. if (!linePointIds.includes(id)) {
  203. const ndx = data.points.findIndex((p) => p.id === id);
  204. ~ndx && data.points.splice(ndx, 1);
  205. }
  206. }
  207. // foreNormalLineData(data)
  208. return deduplicateLines(data);
  209. };
  210. export const genMoveLineHandler = (
  211. data: LineData,
  212. lineId: string,
  213. snapConfig: ReturnType<typeof useSnapConfig>,
  214. snapResult: SnapResultInfo,
  215. ctx = getInitCtx()
  216. ) => {
  217. const line = data.lines.find((line) => line.id === lineId)!;
  218. const pointIds = [line.a, line.b];
  219. const points = pointIds.map((id) => data.points.find((p) => p.id === id)!);
  220. const lineDire = lineVector(points);
  221. const initPoints = copy(points);
  222. const angleRange = [MathUtils.degToRad(10), MathUtils.degToRad(170)];
  223. const getRefInfo = (moveDire: Vector2, ndx: number) => {
  224. const joinLines = getJoinLine(data, line, pointIds[ndx]);
  225. const joinLineDires: Vector2[] = [];
  226. const linePoints = [points[ndx], points[Number(!ndx)]];
  227. const lineDire = lineVector(linePoints);
  228. const joinPoints: LineData["points"] = [];
  229. let invAngle = Number.MAX_VALUE;
  230. let invSelectLineId: string;
  231. let invSelectLineDire: Vector2 | null = null;
  232. let alongAngle = -Number.MAX_VALUE;
  233. let alongSelectLineId: string;
  234. let alongSelectLineDire: Vector2 | null = null;
  235. for (const line of joinLines) {
  236. joinPoints.push(...line.points.filter((p) => !points.includes(p)));
  237. const joinDire = lineVector(line.points);
  238. joinLineDires.push(joinDire);
  239. const angle = vector2IncludedAngle(lineDire, joinDire);
  240. if (angle > 0) {
  241. if (angle < invAngle) {
  242. invAngle = angle;
  243. invSelectLineId = line.id;
  244. invSelectLineDire = joinDire;
  245. }
  246. } else {
  247. if (angle > alongAngle) {
  248. alongAngle = angle;
  249. alongSelectLineId = line.id;
  250. alongSelectLineDire = joinDire;
  251. }
  252. }
  253. }
  254. if (!invSelectLineDire && !alongSelectLineDire) {
  255. return;
  256. }
  257. let isAlong = !invSelectLineDire;
  258. if (!isAlong && alongSelectLineDire) {
  259. const invMoveAngle = Math.abs(
  260. vector2IncludedAngle(moveDire, invSelectLineDire!)
  261. );
  262. const alongMoveAngle = Math.abs(
  263. vector2IncludedAngle(moveDire, alongSelectLineDire!)
  264. );
  265. isAlong = alongMoveAngle! < invMoveAngle!;
  266. }
  267. let info = isAlong
  268. ? {
  269. lineDire,
  270. selectLineDire: alongSelectLineDire!,
  271. selectLineId: alongSelectLineId!,
  272. angle: alongAngle!,
  273. }
  274. : {
  275. lineDire,
  276. selectLineDire: invSelectLineDire!,
  277. selectLineId: invSelectLineId!,
  278. angle: invAngle!,
  279. };
  280. info.angle = rangMod(info.angle, Math.PI);
  281. const needVertical =
  282. info.angle > angleRange[1] || info.angle < angleRange[0];
  283. const needSplit =
  284. needVertical ||
  285. joinLineDires.some(
  286. (dire) =>
  287. dire !== info.selectLineDire &&
  288. !zeroEq(
  289. rangMod(vector2IncludedAngle(dire, info.selectLineDire), Math.PI)
  290. )
  291. );
  292. return { ...info, needSplit, needVertical, joinPoints };
  293. };
  294. let refInfos: ReturnType<typeof getRefInfo>[];
  295. let snapLines: (null | Pos[])[];
  296. let inited = false;
  297. let norNdx = -1;
  298. const init = (moveDires: Vector2[]) => {
  299. refInfos = [getRefInfo(moveDires[0], 0), getRefInfo(moveDires[0], 1)];
  300. snapLines = [];
  301. let minAngle = Math.PI / 2;
  302. const vLineDire = verticalVector(lineDire);
  303. for (let i = 0; i < refInfos.length; i++) {
  304. const refInfo = refInfos[i];
  305. if (!refInfo) {
  306. continue;
  307. }
  308. if (refInfo.needSplit) {
  309. // 拆分点
  310. const point = points[i];
  311. const newPoint = { ...point, id: onlyId() };
  312. data.points.push(newPoint);
  313. repPointRef(data, point.id, newPoint.id, false);
  314. const newLine = {
  315. ...getBaseItem(),
  316. ...defaultStyle,
  317. a: point.id,
  318. b: newPoint.id,
  319. };
  320. data.lines.push(newLine);
  321. ctx.add.lines[newLine.id] = newLine;
  322. ctx.add.points[newPoint.id] = newPoint;
  323. if (i) {
  324. line.b = point.id;
  325. } else {
  326. line.a = point.id;
  327. }
  328. }
  329. const dire = refInfo.needVertical
  330. ? verticalVector(refInfo.selectLineDire)
  331. : refInfo.selectLineDire;
  332. const angle = rangMod(vector2IncludedAngle(dire, vLineDire), Math.PI / 2);
  333. if (angle < minAngle) {
  334. norNdx = i;
  335. minAngle = angle;
  336. }
  337. snapLines[i] = getVectorLine(dire, copy(points[i]), 10);
  338. }
  339. };
  340. const assignPos = (origin: LineData["points"][0], target: Pos) => {
  341. origin.x = target.x;
  342. origin.y = target.y;
  343. ctx.update.points[origin.id] = origin;
  344. };
  345. const updateOtPoint = (ndx: number) => {
  346. const uNdx = ndx === 1 ? 0 : 1;
  347. const move = new Vector2(
  348. points[ndx].x - initPoints[ndx].x,
  349. points[ndx].y - initPoints[ndx].y
  350. );
  351. if (!snapLines[uNdx]) {
  352. assignPos(points[uNdx], move.add(initPoints[uNdx]));
  353. } else {
  354. assignPos(
  355. points[uNdx],
  356. lineIntersection(getVectorLine(lineDire, points[ndx]), snapLines[uNdx])!
  357. );
  358. }
  359. };
  360. const move = (finalPoss: Pos[]) => {
  361. if (!inited) {
  362. const moveDires = finalPoss.map((pos, ndx) =>
  363. lineVector([initPoints[ndx], pos])
  364. );
  365. inited = true;
  366. init(moveDires);
  367. }
  368. if (!snapLines[0] && !snapLines[1]) {
  369. assignPos(points[0], finalPoss[0]);
  370. assignPos(points[1], finalPoss[1]);
  371. } else if (!snapLines[0]) {
  372. const pos = linePointProjection(snapLines[1]!, finalPoss[1]);
  373. assignPos(points[1], pos);
  374. updateOtPoint(1);
  375. } else if (!snapLines[1]) {
  376. const pos = linePointProjection(snapLines[0]!, finalPoss[0]);
  377. assignPos(points[0], pos);
  378. updateOtPoint(0);
  379. } else {
  380. const pos = linePointProjection(snapLines[norNdx]!, finalPoss[norNdx]);
  381. assignPos(points[norNdx], pos);
  382. updateOtPoint(norNdx);
  383. }
  384. };
  385. const getSnapRefPoint = (
  386. point: Pos,
  387. refPoints: Pos[],
  388. line: Pos[] | null
  389. ) => {
  390. for (const refPoint of refPoints) {
  391. if (
  392. lineLen(refPoint, point) < snapConfig.snapOffset &&
  393. (!line || zeroEq(linePointLen(line, refPoint)))
  394. ) {
  395. return refPoint;
  396. }
  397. }
  398. };
  399. const snap = () => {
  400. snapResult.clear();
  401. let refPoint: Pos | undefined = undefined;
  402. let ndx = -1;
  403. const useRefPoint = () => {
  404. const hv = [
  405. { join: refPoint, refDirection: { x: 0, y: 1 } },
  406. { join: refPoint, refDirection: { x: 1, y: 0 } },
  407. ];
  408. snapResult.attractSnaps.push(...(hv as any));
  409. assignPos(points[ndx], refPoint!);
  410. updateOtPoint(ndx);
  411. };
  412. if (refInfos[0]?.joinPoints) {
  413. refPoint = getSnapRefPoint(
  414. points[0],
  415. refInfos[0]?.joinPoints,
  416. snapLines[0]
  417. );
  418. if (refPoint) {
  419. ndx = 0;
  420. return useRefPoint();
  421. }
  422. }
  423. if (refInfos[1]?.joinPoints) {
  424. refPoint = getSnapRefPoint(
  425. points[1],
  426. refInfos[1]?.joinPoints,
  427. snapLines[1]
  428. );
  429. if (refPoint) {
  430. ndx = 1;
  431. return useRefPoint();
  432. }
  433. }
  434. const usedPoints = [
  435. ...(refInfos[0]?.joinPoints || []),
  436. ...(refInfos[1]?.joinPoints || []),
  437. ...points,
  438. ];
  439. const refPoints = data.points.filter((p) => !usedPoints.includes(p));
  440. for (let i = 0; i < points.length; i++) {
  441. refPoint = getSnapRefPoint(points[i], refPoints, snapLines[i]);
  442. if (refPoint) {
  443. ndx = i;
  444. return useRefPoint();
  445. }
  446. }
  447. };
  448. const end = () => {
  449. snapResult.clear();
  450. };
  451. return {
  452. move: (ps: Pos[]) => {
  453. move(ps);
  454. snap();
  455. },
  456. end,
  457. };
  458. };
  459. export const useLineDataSnapInfos = () => {
  460. const infos = useCustomSnapInfos();
  461. const store = useStore();
  462. const lineData = computed(() => store.getTypeItems("line")[0]);
  463. let snapInfos: ComponentSnapInfo[];
  464. const updateSnapInfos = (pointIds: string[]) => {
  465. clear();
  466. snapInfos = getSnapInfos({
  467. ...lineData.value,
  468. lines: lineData.value.lines.filter(
  469. (item) => !(pointIds.includes(item.a) || pointIds.includes(item.b))
  470. ),
  471. points: lineData.value.points.filter(
  472. (item) => !pointIds.includes(item.id)
  473. ),
  474. });
  475. snapInfos.forEach((item) => {
  476. infos.add(item);
  477. });
  478. };
  479. const clear = () => {
  480. snapInfos && snapInfos.forEach((item) => infos.remove(item));
  481. };
  482. return {
  483. update: updateSnapInfos,
  484. clear,
  485. };
  486. };
  487. export const updateLineLength = (
  488. lineData: LineData,
  489. line: LineDataLine,
  490. length: number,
  491. flex?: "a" | "b" | "both",
  492. vector?: Pos
  493. ) => {
  494. const points = [
  495. lineData.points.find((p) => p.id === line.a)!,
  496. lineData.points.find((p) => p.id === line.b)!,
  497. ];
  498. vector = vector || lineVector(points);
  499. if (!flex) {
  500. const aCount = lineData.lines.filter(
  501. (line) => line.a === points[0].id || line.b === points[0].id
  502. ).length;
  503. const bCount = lineData.lines.filter(
  504. (line) => line.a === points[1].id || line.b === points[1].id
  505. ).length;
  506. if (aCount === bCount || (aCount > 1 && bCount > 1)) {
  507. flex = "both";
  508. } else {
  509. flex = aCount > 1 ? "b" : "a";
  510. }
  511. }
  512. let moveVector = new Vector2(vector.x, vector.y);
  513. let npoints: Pos[];
  514. if (flex === "both") {
  515. const center = lineCenter(points);
  516. const l1 = getVectorLine(
  517. moveVector.clone().multiplyScalar(-1),
  518. center,
  519. length / 2
  520. );
  521. const l2 = getVectorLine(moveVector, center, length / 2);
  522. npoints = [l1[1], l2[1]];
  523. } else {
  524. const fNdx = flex === "a" ? 1 : 0;
  525. const mNdx = flex === "a" ? 0 : 1;
  526. const line = getVectorLine(
  527. mNdx === 1 ? moveVector : moveVector.multiplyScalar(-1),
  528. points[fNdx],
  529. length
  530. );
  531. const nPoints: Pos[] = [];
  532. nPoints[fNdx] = points[fNdx];
  533. nPoints[mNdx] = line[1];
  534. npoints = nPoints;
  535. }
  536. Object.assign(points[0], npoints[0]);
  537. Object.assign(points[1], npoints[1]);
  538. };
  539. export const useLineDescribes = (line: Ref<LineDataLine>) => {
  540. const d: any = useComponentDescribes(line, ["stroke", "strokeWidth"], {});
  541. const store = useStore();
  542. const lineData = computed(() => store.getTypeItems("line")[0]);
  543. const points = computed(() => [
  544. lineData.value.points.find((p) => p.id === line.value.a)!,
  545. lineData.value.points.find((p) => p.id === line.value.b)!,
  546. ]);
  547. let setLineVector: Vector2;
  548. watch(d, (d) => {
  549. d.strokeWidth.props = {
  550. ...d.strokeWidth.props,
  551. proportion: true,
  552. };
  553. d.strokeWidth.label = "粗细";
  554. d.stroke.label = "颜色";
  555. d.length = {
  556. type: "inputNum",
  557. label: "线段长度",
  558. "layout-type": "row",
  559. props: {
  560. proportion: true,
  561. },
  562. get value() {
  563. return lineLen(points.value[0], points.value[1]);
  564. },
  565. set value(val) {
  566. console.log(val, d.length.isChange);
  567. if (!d.isChange) {
  568. setLineVector = lineVector(points.value);
  569. }
  570. updateLineLength(
  571. lineData.value,
  572. line.value,
  573. val,
  574. undefined,
  575. setLineVector
  576. );
  577. },
  578. };
  579. }, {immediate: true});
  580. return d as PropertyDescribes;
  581. };
  582. export const useDrawLinePoint = (
  583. data: Ref<LineData>,
  584. line: Ref<LineDataLine>,
  585. callback: (data: {
  586. prev: LineDataLine;
  587. next: LineDataLine;
  588. point: LineData["points"][0];
  589. oldIcons: LineIconData[];
  590. newIcons: LineIconData[];
  591. }) => void
  592. ) => {
  593. const mode = useMode();
  594. let __leave: (() => void) | null;
  595. const leave = () => {
  596. if (__leave) {
  597. __leave();
  598. __leave = null;
  599. }
  600. };
  601. useListener("contextmenu", (ev) => ev.button === 2 && setTimeout(leave));
  602. onUnmounted(leave);
  603. const pos = usePointerPos();
  604. const viewInvMat = useViewerInvertTransform();
  605. const drawProps = ref<{
  606. data: LineData;
  607. prev: LineDataLine;
  608. next: LineDataLine;
  609. point: LineData["points"][0];
  610. }>();
  611. const runHook = useRunHook();
  612. const snapInfos = useLineDataSnapInfos();
  613. const snap = useSnap();
  614. const stage = useStage();
  615. const store = useStore();
  616. const icons = computed(() =>
  617. store.getTypeItems("lineIcon").filter((item) => item.lineId === line.value.id)
  618. );
  619. const drawStore = useDrawIngData();
  620. const cursor = useCursor();
  621. const enterDraw = () => {
  622. const points = getLinePoints(data.value, line.value)
  623. console.log(points, data.value, line.value)
  624. const cdata: LineData = { ...data.value, points, lines: [] };
  625. const point = reactive({ ...lineCenter(points), id: onlyId() });
  626. const cIcons = icons.value.map((icon) => ({ ...icon, id: onlyId() }));
  627. const iconInfos = icons.value.map((icon) => {
  628. const mat = getLineIconMat(points, icon);
  629. return {
  630. position: { x: mat.m[4], y: mat.m[5] },
  631. size: {
  632. width: icon.endLen - icon.startLen,
  633. height: icon.height,
  634. },
  635. };
  636. });
  637. const prev = { ...line.value, id: onlyId(), b: point.id };
  638. const next = { ...line.value, id: onlyId(), a: point.id };
  639. cdata.lines.push(prev, next);
  640. cdata.points.push(point);
  641. drawProps.value = { data: cdata, prev, next, point };
  642. let isStop = false;
  643. const afterUpdate = frameEebounce((position: Pos) => {
  644. if (isStop) return;
  645. snap.clear();
  646. position = viewInvMat.value.point(position);
  647. const mat = snap.move(generateSnapInfos([position], true, true));
  648. Object.assign(point, mat ? mat.point(position) : position);
  649. drawStore.lineIcon = [];
  650. cIcons.forEach((icon, ndx) => {
  651. const getAttach = genGetLineIconAttach(cdata, iconInfos[ndx].size, 200);
  652. const attach = getAttach(iconInfos[ndx].position);
  653. if (attach) {
  654. const line = cdata.lines.find((item) => item.id === attach.lineId)!;
  655. const snapLine = [
  656. cdata.points.find((p) => p.id === line.a)!,
  657. cdata.points.find((p) => p.id === line.b)!,
  658. ];
  659. const iconData = { ...icon, ...attach, __snapLine: snapLine };
  660. drawStore.lineIcon!.push(iconData);
  661. }
  662. });
  663. });
  664. pos.replay();
  665. snapInfos.update([]);
  666. return mergeFuns(
  667. cursor.push("./icons/m_add.png"),
  668. runHook(() =>
  669. clickListener(stage.value!.getNode().container(), () => {
  670. callback({
  671. prev,
  672. next,
  673. point,
  674. oldIcons: icons.value,
  675. newIcons: drawStore.lineIcon!,
  676. });
  677. leave();
  678. })
  679. ),
  680. watch(
  681. pos,
  682. (pos) => {
  683. pos && afterUpdate(pos);
  684. },
  685. { immediate: true }
  686. ),
  687. () => {
  688. drawProps.value = undefined;
  689. snapInfos.clear();
  690. snap.clear();
  691. isStop = true;
  692. }
  693. );
  694. };
  695. const enter = () => {
  696. __leave = mergeFuns(
  697. () => (__leave = null),
  698. mode.push(Mode.draw),
  699. watch(
  700. () => mode.include(Mode.draw),
  701. (hasDraw, _, onCleanup) => {
  702. hasDraw ? onCleanup(enterDraw()) : leave();
  703. },
  704. { immediate: true }
  705. )
  706. );
  707. };
  708. return {
  709. leave,
  710. enter,
  711. drawProps,
  712. };
  713. };