index.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. import { lineVector, Pos, vectorAngle, verticalVector } from "@/utils/math.ts";
  2. import { BaseItem, generateSnapInfos, getBaseItem } from "../util.ts";
  3. import { getMouseColors } from "@/utils/colors.ts";
  4. import { InteractiveFix, InteractiveTo, MatResponseProps } from "../index.ts";
  5. import { copy, inRevise, onlyId, rangMod } from "@/utils/shared.ts";
  6. import { MathUtils } from "three";
  7. import { DrawStore, useStore } from "@/core/store/index.ts";
  8. import {
  9. SelectionManageBus,
  10. UseGetSelectionManage,
  11. } from "@/core/hook/use-selection.ts";
  12. import { EntityShape } from "@/deconstruction.js";
  13. import mitt from "mitt";
  14. import { Ref, ref, watch } from "vue";
  15. import { getInitCtx, NLineDataCtx, normalLineData } from "./attach-server.ts";
  16. import * as wallRenderer from "./renderer/wall";
  17. import { Group } from "konva/lib/Group";
  18. export { default as Component } from "./line.vue";
  19. export { default as TempComponent } from "./temp-line.vue";
  20. export { useDraw } from "./use-draw.ts";
  21. export const shapeName = "墙";
  22. export const defaultStyle = {
  23. stroke: "#000000",
  24. strokeWidth: 20,
  25. fixed: true,
  26. dash: [30, 0],
  27. };
  28. export const renderer = ref<{
  29. genGetShapeAttrib: (data: Ref<LineData | undefined>) => any;
  30. Component: any;
  31. }>();
  32. export const setRendererType = async (type?: "wall" | "db-line") => {
  33. if (type === "wall") {
  34. renderer.value = wallRenderer;
  35. } else {
  36. renderer.value = undefined;
  37. }
  38. };
  39. setRendererType("wall");
  40. export const addMode = "single-dots";
  41. export const getMouseStyle = (data: LineData) => {
  42. const strokeStatus = getMouseColors(data.stroke || defaultStyle.stroke);
  43. const strokeWidth = data.strokeWidth || defaultStyle.strokeWidth;
  44. return {
  45. default: { stroke: data.stroke || defaultStyle.stroke, strokeWidth },
  46. hover: { stroke: strokeStatus.hover },
  47. select: { stroke: strokeStatus.select },
  48. focus: { stroke: strokeStatus.hover },
  49. press: { stroke: strokeStatus.press },
  50. };
  51. };
  52. export const getSnapInfos = (data: LineData) => {
  53. const vh = generateSnapInfos(getSnapPoints(data), true, false, true);
  54. data.lines.forEach((item) => {
  55. const a = data.points.find((p) => p.id === item.a)!;
  56. const b = data.points.find((p) => p.id === item.b)!;
  57. const prevVector = lineVector([a, b]);
  58. const vLine = verticalVector(prevVector);
  59. vh.push({
  60. point: a,
  61. links: [b],
  62. linkDirections: [prevVector],
  63. linkAngle: [rangMod(MathUtils.radToDeg(vectorAngle(vLine)), 180)],
  64. });
  65. });
  66. return vh;
  67. };
  68. export const getSnapPoints = (data: LineData) => {
  69. return data.points;
  70. };
  71. export type LineDataLine = {
  72. id: string;
  73. a: string;
  74. b: string;
  75. fixed: boolean
  76. strokeWidth: number;
  77. stroke: string;
  78. dash: number[];
  79. };
  80. export type LineDataPoint = Pos & { id: string };
  81. export type LineData = Partial<typeof defaultStyle> &
  82. BaseItem & {
  83. points: LineDataPoint[];
  84. lines: LineDataLine[];
  85. polygon: { points: string[]; id: string }[];
  86. updateTime?: number;
  87. calcTime?: number;
  88. };
  89. export const interactiveToData: InteractiveTo<"line"> = ({
  90. info,
  91. preset = {},
  92. ...args
  93. }) => {
  94. if (info.cur) {
  95. const baseItem = getBaseItem();
  96. return interactiveFixData({
  97. ...args,
  98. info,
  99. data: {
  100. ...defaultStyle,
  101. ...baseItem,
  102. ...preset,
  103. lines: [],
  104. points: [],
  105. polygon: [],
  106. },
  107. });
  108. }
  109. };
  110. export const interactiveFixData: InteractiveFix<"line"> = ({ data, info }) => {
  111. const nv = [...info.consumed, info.cur!];
  112. data.points.length = nv.length;
  113. for (let i = 0; i < nv.length; i++) {
  114. if (inRevise(data.points[i], nv[i])) {
  115. if (!data.points[i]) {
  116. data.points[i] = {
  117. id: onlyId(),
  118. ...nv[i],
  119. };
  120. } else {
  121. data.points[i] = {
  122. ...data.points[i],
  123. ...nv[i],
  124. };
  125. }
  126. }
  127. }
  128. data.lines.length = nv.length - 1;
  129. for (let i = 0; i < nv.length - 1; i++) {
  130. if (!data.lines[i]) {
  131. data.lines[i] = {
  132. id: onlyId(),
  133. ...defaultStyle,
  134. a: data.points[i].id,
  135. b: data.points[i + 1].id,
  136. };
  137. }
  138. }
  139. // data.polygon = [{points: [data.lines.map((item) => item.id)], id: onlyId()}];
  140. return data;
  141. };
  142. const matResPoints = new Set<string>();
  143. let matCtx: NLineDataCtx | null;
  144. export const startMatResponse = () => {
  145. matCtx = getInitCtx();
  146. };
  147. export const matResponse = ({
  148. data,
  149. mat,
  150. operId,
  151. }: MatResponseProps<"line">) => {
  152. const line = data.lines.find((item) => item.id === operId);
  153. if (!line) return;
  154. const ids = [line.a, line.b];
  155. for (const id of ids) {
  156. if (matResPoints.has(id)) {
  157. continue;
  158. }
  159. const ndx = data.points.findIndex((item) => item.id === id);
  160. if (~ndx) {
  161. const point = data.points[ndx];
  162. data.points[ndx] = {
  163. ...point,
  164. ...mat.point(point),
  165. };
  166. matCtx!.update.points[point.id] = data.points[ndx];
  167. matResPoints.add(id);
  168. }
  169. }
  170. return data;
  171. };
  172. export const endMatResponse = () => {
  173. matResPoints.clear();
  174. // matCtx && normalLineData(matData, matCtx)
  175. // console.log(matData, matCtx)
  176. matCtx = null;
  177. };
  178. export const getPredefine = (key: keyof LineData) => {
  179. if (key === "strokeWidth") {
  180. return { proportion: true };
  181. }
  182. };
  183. export const childrenDataGetter = (data: LineData, id: string) => {
  184. const line = data.lines.find((item) => item.id === id);
  185. if (!line) return;
  186. const ids = [line.a, line.b];
  187. return data.points.filter((p) => ids.includes(p.id));
  188. };
  189. export const delItem = (store: DrawStore, data: LineData, childId: string) => {
  190. if (!childId) {
  191. store.delItem("line", data.id);
  192. return;
  193. }
  194. let ndx;
  195. if (~(ndx = data.lines.findIndex((item) => item.id === childId))) {
  196. const delLine = data.lines[ndx];
  197. const ctx = getInitCtx();
  198. ctx.del.lines[delLine.id] = delLine;
  199. data.lines.splice(ndx, 1);
  200. normalLineData(data, ctx);
  201. store.setItem("line", { value: data, id: data.id });
  202. } else if (~(ndx = data.points.findIndex((item) => item.id === childId))) {
  203. const { ctx } = delPoint(data, childId);
  204. normalLineData(data, ctx);
  205. store.setItem("line", { value: data, id: data.id });
  206. }
  207. };
  208. export const delPoint = (data: LineData, id: string, ctx = getInitCtx()) => {
  209. const p = data.points.find((item) => item.id === id);
  210. if (!p) return { data, ctx };
  211. const checkLines = data.lines.filter(
  212. (item) => item.a === p.id || item.b === p.id
  213. );
  214. if (checkLines.length > 1) {
  215. const joinPoints = new Set<string>();
  216. checkLines.forEach((item) => {
  217. joinPoints.add(item.a);
  218. joinPoints.add(item.b);
  219. });
  220. if (joinPoints.size === 3) {
  221. const prev = checkLines.find((item) => item.b === p.id);
  222. const next = checkLines.find((item) => item.a === p.id);
  223. if (prev && next) {
  224. const l = { ...prev, id: onlyId(), b: next.b };
  225. ctx.add.lines[l.id] = l;
  226. data.lines.push(l);
  227. } else {
  228. const l = prev || next || checkLines[0];
  229. const ps = [...joinPoints].filter((item) => item !== p.id);
  230. const nl = { ...l, id: onlyId(), a: ps[0], b: ps[1] };
  231. ctx.add.lines[l.id] = nl;
  232. data.lines.push(nl);
  233. }
  234. }
  235. }
  236. checkLines.forEach((l) => {
  237. ctx.del.lines[l.id] = l;
  238. const ndx = data.lines.findIndex((ln) => ln.id === l.id);
  239. ~ndx && data.lines.splice(ndx, 1);
  240. });
  241. ctx.del.points[p.id] = p;
  242. const ndx = data.points.findIndex((pn) => pn.id === p.id);
  243. ~ndx && data.points.splice(ndx, 1);
  244. return { data, ctx };
  245. };
  246. export const useGetSelectionManage: UseGetSelectionManage = () => {
  247. const store = useStore();
  248. const canSelect = (shape: EntityShape) => {
  249. const id = shape.id();
  250. const line = store.getTypeItems("line")[0];
  251. return !!(id && line.lines.some((item) => item.id === id) && !(shape instanceof Group));
  252. };
  253. const listener = (shape: EntityShape) => {
  254. const bus: SelectionManageBus = mitt();
  255. const stop = watch(
  256. () => canSelect(shape),
  257. (exixts, _) => {
  258. if (!exixts) {
  259. bus.emit("del", shape);
  260. }
  261. },
  262. { immediate: true }
  263. );
  264. return { stop, bus };
  265. };
  266. return { canSelect, listener };
  267. };