whole-line-edit.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. import {
  2. WholeLineAttrib,
  3. WholeLineLine,
  4. WholeLinePoint,
  5. WholeLinePointAttrib,
  6. WholeLinePolygonAttrib,
  7. } from "../view";
  8. import { KonvaEventObject } from "konva/lib/Node";
  9. import {
  10. getFlatChildren,
  11. getRealAbsoluteSize,
  12. round,
  13. shapeParentsEq,
  14. } from "../../../shared";
  15. import {
  16. wholeLineAddPoint,
  17. wholeLineFixLineAddPoint,
  18. wholeLinePolygonLastAddPoint,
  19. } from "./whole-line-mouse";
  20. import { Container } from "../../container";
  21. import { Attrib, ShapeType } from "../../../type";
  22. import {
  23. WholeLineChange,
  24. generateWholeLinePoygonId,
  25. getWholeLinePoint,
  26. getWholeLinePolygonPoints,
  27. mergeChange,
  28. wholeLineAddLineByPointIds,
  29. wholeLineDelPointByPointIds,
  30. wholeLineDelPolygon,
  31. } from "./whole-line-base";
  32. import { getAdsorbPosition } from "../../../shared/adsorb";
  33. export type PenWholeLinePoygonsEditProps<
  34. P extends Omit<WholeLinePointAttrib, "id">
  35. > = {
  36. tree: Container;
  37. onChange?: (change: WholeLineChange) => void;
  38. config: WholeLineAttrib<P & Attrib>;
  39. polygonId?: string;
  40. adsorbRadius?: number;
  41. autoAdd?: boolean;
  42. closeAutoQuit?: boolean;
  43. pointAttribFactory?: (pos: number[]) => Omit<P, "id">;
  44. quotePoint?: boolean | ((point: WholeLinePointAttrib) => boolean);
  45. canOper?: (
  46. tree: WholeLineLine | WholeLinePoint,
  47. operShape: ShapeType
  48. ) => boolean;
  49. canDelPoint?: (point: P, evt: KonvaEventObject<any>) => boolean;
  50. changePolygon?: (polygonId: string) => void;
  51. autoClose?: boolean;
  52. };
  53. /**
  54. * 钢笔模式编辑多边形
  55. * @param polygonId
  56. */
  57. export const penWholeLinePoygonsEdit = <
  58. P extends Omit<WholeLinePointAttrib, "id">
  59. >({
  60. tree,
  61. config,
  62. polygonId,
  63. pointAttribFactory,
  64. quotePoint,
  65. canOper,
  66. autoAdd,
  67. adsorbRadius,
  68. canDelPoint,
  69. closeAutoQuit,
  70. changePolygon,
  71. autoClose,
  72. }: PenWholeLinePoygonsEditProps<P>) => {
  73. pointAttribFactory =
  74. pointAttribFactory ||
  75. ((pos: number[]) =>
  76. ({
  77. x: pos[0],
  78. y: pos[1],
  79. } as P));
  80. const getPolygonAttrib = (polygonId: string) => {
  81. if (!polygonId) {
  82. const id = generateWholeLinePoygonId(config);
  83. config.polygons.push({
  84. id,
  85. lineIds: [],
  86. });
  87. return config.polygons[config.polygons.length - 1];
  88. } else {
  89. return config.polygons.find(({ id }) => id === polygonId);
  90. }
  91. };
  92. let polyginAttrib: WholeLinePolygonAttrib;
  93. let newMode = false;
  94. let prevId: string | null = null;
  95. let canAddPolygon = true;
  96. const start = (currentPolygonId: string | null) => {
  97. polyginAttrib = getPolygonAttrib(currentPolygonId);
  98. if (!polyginAttrib) {
  99. throw `${currentPolygonId}的多边形不存在!`;
  100. }
  101. changePolygon && changePolygon(polyginAttrib.id);
  102. polygonId = polyginAttrib.id;
  103. newMode = !currentPolygonId;
  104. prevId = null;
  105. canAddPolygon = autoAdd || polyginAttrib.lineIds.length < 2;
  106. };
  107. start(polygonId);
  108. const removePolygon = () => {
  109. const ndx = config.polygons.indexOf(polyginAttrib);
  110. if (~ndx) {
  111. config.polygons.splice(ndx, 1);
  112. }
  113. };
  114. const continuous = (
  115. evt: KonvaEventObject<any>
  116. ): { change: WholeLineChange; isClose?: boolean } => {
  117. let change: WholeLineChange = {
  118. lineChange: {
  119. del: [],
  120. update: [],
  121. add: [],
  122. },
  123. polygonChange: {
  124. add: [],
  125. update: [],
  126. del: [],
  127. },
  128. pointChange: {
  129. add: [],
  130. del: [],
  131. },
  132. };
  133. const target = shapeParentsEq(evt.target, (shape) => {
  134. const id = shape.id();
  135. return (
  136. id.includes(WholeLineLine.namespace) ||
  137. id.includes(WholeLinePoint.namespace)
  138. );
  139. });
  140. let child = target && tree.find(target.id());
  141. const pixel = [evt.evt.offsetX, evt.evt.offsetY];
  142. console.log(child);
  143. if (child instanceof WholeLineLine) {
  144. if (!canOper || canOper(child, evt.target)) {
  145. if (polyginAttrib.lineIds.includes(child.attrib.id)) {
  146. const { change: cChange } = wholeLineFixLineAddPoint(
  147. config,
  148. child.attrib.id,
  149. pointAttribFactory(tree.getRealFromStage(pixel))
  150. );
  151. return { change: mergeChange(change, cChange), isClose: false };
  152. }
  153. }
  154. }
  155. let pointAttrib: P & Attrib;
  156. const polygonPoints = getWholeLinePolygonPoints(config, polyginAttrib.id);
  157. let isQuotePoint;
  158. while (true) {
  159. if (child instanceof WholeLinePoint) {
  160. if (canOper && !canOper(child, evt.target)) {
  161. return { change, isClose: false };
  162. }
  163. isQuotePoint =
  164. typeof quotePoint === "function"
  165. ? quotePoint(child.attrib)
  166. : !!quotePoint;
  167. pointAttrib = isQuotePoint ? child.attrib : { ...child.attrib };
  168. if (polyginAttrib.id === prevId) {
  169. return { change, isClose: false };
  170. }
  171. } else {
  172. let position: number[];
  173. if (adsorbRadius) {
  174. const points = polygonPoints.map(({ x, y }) => [x, y]);
  175. if (prevId) {
  176. const prev = getWholeLinePoint(config, prevId);
  177. points.push([prev.x, prev.y]);
  178. }
  179. position = getAdsorbPosition({
  180. tree,
  181. pixel,
  182. radius: adsorbRadius,
  183. points: [...points].reverse(),
  184. });
  185. console.log(position);
  186. } else {
  187. position = tree.getRealFromStage(pixel);
  188. }
  189. const flatChildren = getFlatChildren(tree);
  190. let i = 0;
  191. for (; i < flatChildren.length; i++) {
  192. if (flatChildren[i] instanceof WholeLinePoint) {
  193. const point = flatChildren[i] as WholeLinePoint;
  194. if (
  195. round(point.attrib.x - position[0], 8) === 0 &&
  196. round(point.attrib.y - position[1], 8) === 0
  197. ) {
  198. child = tree.children[i];
  199. break;
  200. }
  201. }
  202. }
  203. if (i !== flatChildren.length) {
  204. child = flatChildren[i];
  205. continue;
  206. }
  207. pointAttrib = pointAttribFactory(position) as P & Attrib;
  208. }
  209. break;
  210. }
  211. const curNdx = polygonPoints.findIndex(({ id }) => id === pointAttrib.id);
  212. const isClose = curNdx === 0 && (!autoClose || polygonPoints.length >= 3);
  213. // 存在的情况下删除
  214. if (curNdx >= 0 && !isClose) {
  215. const cChange = wholeLineDelPointByPointIds(
  216. config,
  217. pointAttrib.id,
  218. !canDelPoint || canDelPoint(pointAttrib, evt)
  219. );
  220. change = mergeChange(change, cChange);
  221. if (polyginAttrib.lineIds.length === 0) {
  222. wholeLineDelPolygon(config, polyginAttrib.id);
  223. const repPoint = cChange.pointChange.del.find(
  224. ({ id }) => id !== pointAttrib.id
  225. );
  226. start(null);
  227. const prev = wholeLineAddPoint(config, { ...repPoint, id: undefined });
  228. prevId = prev.id;
  229. change.pointChange.add.push(prev);
  230. }
  231. return { change, isClose };
  232. }
  233. if (!isQuotePoint) {
  234. delete pointAttrib.id;
  235. }
  236. const afterHandler = (iChange: WholeLineChange) => {
  237. // 线段闭合重新启一个多边形
  238. if (!closeAutoQuit && isClose) {
  239. start(null);
  240. }
  241. return { change: iChange, isClose };
  242. };
  243. if (polyginAttrib.lineIds.length > 0) {
  244. if (newMode) {
  245. const mChange = wholeLinePolygonLastAddPoint(
  246. config,
  247. polygonId,
  248. pointAttrib
  249. ).change;
  250. // 持续添加模式
  251. return afterHandler(mergeChange(change, mChange));
  252. } else {
  253. if (canAddPolygon) {
  254. // 直接当成新建操作
  255. start(null);
  256. return continuous(evt);
  257. } else {
  258. return { change, isClose };
  259. }
  260. }
  261. } else if (prevId) {
  262. const [offset] = getRealAbsoluteSize(tree.stage, [4, 4], true);
  263. const prev = getWholeLinePoint(config, prevId);
  264. if (
  265. !(
  266. Math.abs(pointAttrib.x - prev.x) > offset ||
  267. Math.abs(pointAttrib.y - prev.y) > offset
  268. )
  269. ) {
  270. return afterHandler(change);
  271. }
  272. const { line, change: mChange } = wholeLineAddLineByPointIds(config, [
  273. prevId,
  274. wholeLineAddPoint(config, pointAttrib).id,
  275. ]);
  276. change = mergeChange(change, mChange, {
  277. polygonChange: {
  278. update: [
  279. {
  280. before: { ...polyginAttrib, lineIds: [...polyginAttrib.lineIds] },
  281. after: {
  282. ...polyginAttrib,
  283. lineIds: [...polyginAttrib.lineIds, line.id],
  284. },
  285. },
  286. ],
  287. },
  288. pointChange: {
  289. add: [pointAttrib],
  290. },
  291. });
  292. polyginAttrib.lineIds.push(line.id);
  293. prevId = null;
  294. return afterHandler(change);
  295. } else {
  296. const addPoint = wholeLineAddPoint(config, pointAttrib)!;
  297. prevId = addPoint.id;
  298. change.pointChange.add.push(addPoint);
  299. return afterHandler(change);
  300. }
  301. };
  302. const end = () => {
  303. // 没有两个点以上的多边形直接删除
  304. if (polyginAttrib.lineIds.length === 0) {
  305. if (prevId) {
  306. wholeLineDelPointByPointIds(config, prevId);
  307. }
  308. removePolygon();
  309. }
  310. changePolygon && changePolygon(null);
  311. };
  312. const getStatus = () => ({
  313. newMode,
  314. polyginAttribId: polyginAttrib.id,
  315. prevId,
  316. config,
  317. });
  318. return {
  319. continuous,
  320. end,
  321. getStatus,
  322. setStatus: (status: ReturnType<typeof getStatus>) => {
  323. newMode = status.newMode;
  324. polyginAttrib = status.config.polygons.find(
  325. ({ id }) => id === status.polyginAttribId
  326. );
  327. polygonId = polyginAttrib.id;
  328. prevId = status.prevId;
  329. config = status.config;
  330. },
  331. };
  332. };