use-dxf.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import {
  2. DxfWriter,
  3. point2d,
  4. LWPolylineFlags,
  5. point3d,
  6. TrueColor,
  7. pattern,
  8. HatchPredefinedPatterns,
  9. HatchBoundaryPaths,
  10. HatchPolylineBoundary,
  11. vertex,
  12. } from "@tarikjabiri/dxf";
  13. import { useStore } from "../store";
  14. import Zip from "jszip";
  15. import { LineData } from "../components/line";
  16. import { CircleData } from "../components/circle";
  17. import { Transform } from "konva/lib/Util";
  18. import { lineVector, Pos, Size, verticalVectorLine } from "@/utils/math";
  19. import { RectangleData } from "../components/rectangle";
  20. import { useStage } from "./use-global-vars";
  21. import { Group } from "konva/lib/Group";
  22. import { Text } from "konva/lib/shapes/Text";
  23. import { TextData } from "../components/text";
  24. import { ImageData } from "../components/image";
  25. import { onlyId } from "@/utils/shared";
  26. import { ArrowData, PointerPosition } from "../components/arrow";
  27. import { IconData } from "../components/icon";
  28. import {
  29. useSetViewport,
  30. useViewer,
  31. useViewerInvertTransform,
  32. } from "./use-viewer";
  33. import { nextTick } from "vue";
  34. import { SLineData } from "../components/sequent-line";
  35. export const useGetDXF = () => {
  36. const store = useStore();
  37. const stage = useStage();
  38. const invMat = useViewerInvertTransform();
  39. const { setViewport } = useSetViewport();
  40. const { viewer } = useViewer();
  41. return async () => {
  42. const writer = new DxfWriter();
  43. const $stage = stage.value!.getNode();
  44. const zip = new Zip();
  45. const genPromises: Promise<any>[] = [];
  46. const fontStyle = writer.tables.styleTable.records[0];
  47. fontStyle.fontFileName = "SimSun";
  48. type PL = {
  49. id?: string;
  50. points: Pos[];
  51. fill?: string;
  52. content?: string;
  53. strokeWidth?: number;
  54. stroke?: string;
  55. };
  56. const writerPolyline = (pl: PL) => {
  57. if (pl.fill) {
  58. const polyline = new HatchPolylineBoundary();
  59. const boundary = new HatchBoundaryPaths();
  60. pl.points.forEach((p) => polyline.add(vertex(p.x, -p.y)));
  61. boundary.addPolylineBoundary(polyline);
  62. writer.addHatch(
  63. boundary,
  64. pattern({ name: HatchPredefinedPatterns.SOLID }),
  65. { trueColor: TrueColor.fromHex(pl.fill).toString() }
  66. );
  67. }
  68. writer.addLWPolyline(
  69. pl.points.map((p) => ({ point: point2d(p.x, -p.y) })),
  70. {
  71. flags: LWPolylineFlags.Closed,
  72. constantWidth: pl.strokeWidth,
  73. trueColor: TrueColor.fromHex(pl.stroke || "#FFFFFF").toString(),
  74. }
  75. );
  76. if (!pl.content) return;
  77. const $text = $stage.findOne<Group>(`#${pl.id}`)?.findOne<Text>(".text");
  78. if ($text) {
  79. writeText($text);
  80. }
  81. };
  82. const writeText = ($text: Text, sp = true) => {
  83. const mat = $text.getTransform();
  84. const fontSize = $text.fontSize() * 0.8;
  85. let text = $text.text();
  86. text = text.replace(/(\n|\r|\t)/gi, "");
  87. const pad = $text.padding();
  88. const align = $text.align();
  89. const textArr: { content: string; charCount: number }[] = [];
  90. let lineNum = 1;
  91. const width = $text.width();
  92. const charWidth = fontSize * 0.9;
  93. console.log(text);
  94. const lineCharCount = sp
  95. ? Math.max(Math.floor(width / charWidth), 2)
  96. : Number.MAX_VALUE;
  97. let ndx = 0;
  98. let prevNdx = 0;
  99. let charCount = 0;
  100. while (ndx < text.length) {
  101. ndx++;
  102. const c = /[\u4e00-\u9fff]/.test(text.charAt(ndx)) ? 2.4 : 1;
  103. if (charCount === lineCharCount || ndx >= text.length) {
  104. charCount += c;
  105. textArr.push({
  106. content: text.substring(prevNdx, ndx + 1),
  107. charCount,
  108. });
  109. charCount = 0;
  110. prevNdx = ndx;
  111. } else if (charCount > lineCharCount) {
  112. textArr.push({
  113. content: text.substring(prevNdx, ndx),
  114. charCount,
  115. });
  116. charCount = 0;
  117. ndx--;
  118. prevNdx = ndx;
  119. } else {
  120. charCount += c;
  121. }
  122. }
  123. console.log(textArr);
  124. textArr.forEach((item) => {
  125. const lineWidth = charWidth * item.charCount;
  126. let p = { x: pad, y: pad + lineNum * fontSize * 1.2 };
  127. if (align === "center") {
  128. p.x = (width - lineWidth) / 2;
  129. } else if (align === "right") {
  130. p.x = width - lineWidth;
  131. }
  132. const start = mat.point(p);
  133. console.log(point3d(start.x, -start.y), fontSize, item.content, {
  134. rotation: $text.rotation(),
  135. // horizontalAlignment:
  136. // align === "center"
  137. // ? TextHorizontalAlignment.Center
  138. // : align === "right"
  139. // ? TextHorizontalAlignment.Right
  140. // : TextHorizontalAlignment.Left,
  141. });
  142. const text = writer.addText(
  143. point3d(start.x, -start.y),
  144. fontSize,
  145. item.content,
  146. {
  147. rotation: $text.rotation(),
  148. // horizontalAlignment:
  149. // align === "center"
  150. // ? TextHorizontalAlignment.Center
  151. // : align === "right"
  152. // ? TextHorizontalAlignment.Right
  153. // : TextHorizontalAlignment.Left,
  154. }
  155. );
  156. text.trueColor = TrueColor.fromHex($text.fill() as string).toString();
  157. text.height = fontSize;
  158. lineNum++;
  159. });
  160. };
  161. const writeImage = async (imgGroup: Group, minSize: Size) => {
  162. let rect = imgGroup.getClientRect();
  163. const oldViewMat = viewer.viewMat;
  164. setViewport(rect);
  165. await nextTick();
  166. rect = imgGroup.getClientRect();
  167. const img = (await imgGroup!.toImage({
  168. pixelRatio: 1,
  169. quality: 1,
  170. mimeType: "image/png",
  171. })) as HTMLImageElement;
  172. const start = invMat.value.point({ x: rect.x, y: rect.y + rect.height });
  173. const end = invMat.value.point({ x: rect.x + rect.width, y: rect.y });
  174. const name = onlyId().replace(/\-/g, "");
  175. const path = name + ".png";
  176. const image = writer.addImage(
  177. path,
  178. name,
  179. point3d(start.x, -start.y),
  180. img.width,
  181. img.height,
  182. 1,
  183. 0
  184. );
  185. image.ratio = Math.abs(end.x - start.x) / img.width;
  186. genPromises.push(
  187. fetch(img.src)
  188. .then((res) => res.blob())
  189. .then((blob) => zip.file(path, blob, {}))
  190. );
  191. viewer.setViewMat(oldViewMat);
  192. };
  193. for (const _item of store.sortItems) {
  194. if (_item.hide) continue;
  195. const type = store.getType(_item.id);
  196. let item;
  197. let mat;
  198. switch (type) {
  199. case "sequentLine":
  200. item = _item as SLineData;
  201. writer.addLWPolyline(
  202. item.points.map((p) => ({ point: point2d(p.x, -p.y) })),
  203. {
  204. flags: LWPolylineFlags.None,
  205. constantWidth: item.strokeWidth,
  206. trueColor: TrueColor.fromHex(item.stroke || "#FFFFFF").toString(),
  207. }
  208. );
  209. break;
  210. case "line":
  211. const litem = _item as LineData;
  212. litem.lines.forEach((line) => {
  213. const a = litem.points.find((p) => p.id === line.a)!;
  214. const b = litem.points.find((p) => p.id === line.b)!;
  215. // writer.addLine(
  216. // point3d(a.x, a.y, 0),
  217. // point3d(b.x, b.y, 0),
  218. // {
  219. // trueColor: TrueColor.fromHex(line.stroke || "#FFFFFF").toString(),
  220. // }
  221. // )
  222. writer.addLWPolyline(
  223. [
  224. { point: point3d(a.x, -a.y, 0) },
  225. { point: point3d(b.x, -b.y, 0) },
  226. ],
  227. {
  228. flags: LWPolylineFlags.None,
  229. constantWidth: line.strokeWidth,
  230. trueColor: TrueColor.fromHex(
  231. line.stroke || "#FFFFFF"
  232. ).toString(),
  233. }
  234. );
  235. });
  236. break;
  237. case "triangle":
  238. case "rectangle":
  239. case "polygon":
  240. item = _item as RectangleData;
  241. writerPolyline(item);
  242. break;
  243. case "circle":
  244. item = _item as CircleData;
  245. mat = new Transform(item.mat);
  246. const points = [];
  247. for (let angle = 0; angle <= 360; angle += 1) {
  248. const rad = angle * (Math.PI / 180);
  249. const x = item.radiusX * Math.cos(rad);
  250. const y = item.radiusY * Math.sin(rad);
  251. points.push(mat.point({ x, y }));
  252. }
  253. writerPolyline({ ...item, points });
  254. break;
  255. case "text":
  256. item = _item as TextData;
  257. writeText($stage.findOne<Text>(`#${item.id}`)!, !!item.width);
  258. break;
  259. case "arrow":
  260. item = _item as ArrowData;
  261. // writer.addLWPolyline(
  262. // item.points.map((p) => ({ point: point2d(p.x, -p.y) })),
  263. // {
  264. // flags: LWPolylineFlags.None,
  265. // constantWidth: item.strokeWidth,
  266. // trueColor: TrueColor.fromHex(item.fill || "#FFFFFF").toString(),
  267. // }
  268. // );
  269. const isEnd = [PointerPosition.end, PointerPosition.all].includes(
  270. item.pointerPosition || PointerPosition.start
  271. );
  272. const isStart = [PointerPosition.start, PointerPosition.all].includes(
  273. item.pointerPosition || PointerPosition.start
  274. );
  275. for (let i = 0; i < item.points.length - 1; i++) {
  276. const line = [item.points[i], item.points[i + 1]];
  277. const nline = [...line];
  278. const vector = lineVector(line);
  279. if (isStart) {
  280. const start = vector
  281. .clone()
  282. .multiplyScalar(item.pointerLength!)
  283. .add(line[0]);
  284. nline[0] = start;
  285. const l1 = verticalVectorLine(
  286. vector,
  287. start,
  288. item.pointerLength! / 2
  289. );
  290. const l2 = verticalVectorLine(
  291. vector,
  292. start,
  293. -item.pointerLength! / 2
  294. );
  295. writerPolyline({
  296. points: [line[0], l1[1], l2[1]],
  297. fill: item.fill,
  298. stroke: item.fill,
  299. });
  300. }
  301. if (isEnd) {
  302. const start = vector
  303. .clone()
  304. .multiplyScalar(-item.pointerLength!)
  305. .add(line[1]);
  306. nline[1] = start;
  307. const l1 = verticalVectorLine(
  308. vector,
  309. start,
  310. item.pointerLength! / 2
  311. );
  312. const l2 = verticalVectorLine(
  313. vector,
  314. start,
  315. -item.pointerLength! / 2
  316. );
  317. writerPolyline({
  318. points: [line[1], l1[1], l2[1]],
  319. fill: item.fill,
  320. stroke: item.fill,
  321. });
  322. }
  323. writer.addLWPolyline(
  324. nline.map((p) => ({ point: point2d(p.x, -p.y) })),
  325. {
  326. flags: LWPolylineFlags.None,
  327. constantWidth: item.strokeWidth,
  328. trueColor: TrueColor.fromHex(item.fill || "#FFFFFF").toString(),
  329. }
  330. );
  331. }
  332. break;
  333. case "image":
  334. item = _item as ImageData;
  335. await writeImage($stage.findOne<Group>(`#${item.id}`)!, item);
  336. break;
  337. case "icon":
  338. item = _item as IconData;
  339. const pathGroup = $stage
  340. .findOne<Group>(`#${item.id}`)!
  341. .findOne<Group>(".rep-position")!;
  342. await writeImage(pathGroup, item);
  343. break;
  344. }
  345. }
  346. let dxfString = writer.stringify();
  347. zip.file(onlyId() + ".dxf", dxfString);
  348. return Promise.all(genPromises).then(() =>
  349. zip.generateAsync({ type: "blob" })
  350. );
  351. };
  352. };