build-dxf 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/build.js CHANGED
@@ -1558,7 +1558,8 @@ class LineSegment {
1558
1558
  const [q1, q2] = this.points;
1559
1559
  const dir = new Point(q2.x - q1.x, q2.y - q1.y);
1560
1560
  if (dir.x === 0 && dir.y === 0) {
1561
- throw new Error("投影目标线段的两个点不能重合");
1561
+ console.error("投影目标线段的两个点不能重合");
1562
+ return new LineSegment();
1562
1563
  }
1563
1564
  const projectPoint = (point) => {
1564
1565
  const pq = new Point(point.x - q1.x, point.y - q1.y);
@@ -3079,7 +3080,7 @@ class LineGroupType {
3079
3080
  return false;
3080
3081
  }
3081
3082
  /**
3082
- * 获取所有线段的交集类型(所有线段都包含的类型)
3083
+ * 通过类型移除
3083
3084
  * @param line
3084
3085
  * @param id
3085
3086
  * @returns
@@ -3935,6 +3936,10 @@ function buildDoubleWallGroup(lines, doorLines = [], grouped = true, clearIntern
3935
3936
  let { newLines, rings } = findLargestCircle(lines), ringsSet = new Set(rings.flat());
3936
3937
  if (grouped) {
3937
3938
  const grid = new PointVirtualGrid(), internalEdges = /* @__PURE__ */ new Set();
3939
+ lines.forEach((line) => {
3940
+ LineGroupType.removeByType(line, "doubleWall");
3941
+ LineGroupType.removeByType(line, "wall");
3942
+ });
3938
3943
  newLines.forEach((line) => !ringsSet.has(line) && grid.insert(line.center, line));
3939
3944
  doorLines.forEach((line) => grid.insert(line.center, line));
3940
3945
  const id = uuid();
@@ -3944,15 +3949,13 @@ function buildDoubleWallGroup(lines, doorLines = [], grouped = true, clearIntern
3944
3949
  rings.forEach((group2) => {
3945
3950
  const id2 = uuid();
3946
3951
  group2.forEach((line) => {
3947
- if (!LineGroupType.replace(line, id2, "doubleWall")) LineGroupType.set(line, id2, "doubleWall");
3948
- LineGroupType.removeByType(line, "wall");
3952
+ LineGroupType.set(line, id2, "doubleWall");
3949
3953
  });
3950
3954
  clearInternalLine && grid.queryPolygon(Polygon.fromByLines(group2)).forEach((res) => internalEdges.add(res.userData));
3951
3955
  });
3952
3956
  newLines.forEach((line) => {
3953
3957
  if (ringsSet.has(line) || LineGroupType.hasType(line, "bayWindow")) return;
3954
3958
  LineGroupType.set(line, "default", "wall");
3955
- LineGroupType.removeByType(line, "doubleWall");
3956
3959
  });
3957
3960
  newLines = newLines.filter((line) => !internalEdges.has(line));
3958
3961
  const newDoorLines = doorLines.filter((line) => !internalEdges.has(line));
@@ -4854,7 +4857,7 @@ class Group {
4854
4857
  }
4855
4858
  }
4856
4859
  const defaultWallWidth = 0.12;
4857
- const units$1 = {
4860
+ const units = {
4858
4861
  Unitless: 1,
4859
4862
  // 无单位,1米 = 1(无单位)
4860
4863
  Inches: 39.37007874015748,
@@ -5091,7 +5094,7 @@ class CAD {
5091
5094
  toDrawData(unit = "Millimeters") {
5092
5095
  if (!this.needUpdate && this._cachedDrawData) return this._cachedDrawData;
5093
5096
  this.needUpdate = false;
5094
- const lines = this.groups.flatMap((group2) => group2.lines), s = units$1[unit], expansionWidth = 2, box = Box2.fromByLineSegment(...lines).expansion(expansionWidth), center = box.center, data = {
5097
+ const lines = this.groups.flatMap((group2) => group2.lines), s = units[unit], expansionWidth = 2, box = Box2.fromByLineSegment(...lines).expansion(expansionWidth), center = box.center, data = {
5095
5098
  unit,
5096
5099
  lines: [],
5097
5100
  arcs: [],
@@ -5653,7 +5656,7 @@ class BoundExt {
5653
5656
  };
5654
5657
  }
5655
5658
  }
5656
- const PRE_PROCESSOR = {
5659
+ const PRE_PROCESSOR$1 = {
5657
5660
  DoorsAnalysis(lines) {
5658
5661
  return lines;
5659
5662
  },
@@ -5704,7 +5707,7 @@ const PRE_PROCESSOR = {
5704
5707
  };
5705
5708
  class CorrectionDxf extends Component {
5706
5709
  static name = "CorrectionDxf";
5707
- static PRE_PROCESSOR = PRE_PROCESSOR;
5710
+ static PRE_PROCESSOR = PRE_PROCESSOR$1;
5708
5711
  width = 0.04;
5709
5712
  originalData = [];
5710
5713
  data = [];
@@ -5735,8 +5738,8 @@ class CorrectionDxf extends Component {
5735
5738
  return this;
5736
5739
  }
5737
5740
  onAddFromParent(parent) {
5738
- parent.Dxf.addEventListener("lineOffset", () => {
5739
- this.trajectory && this.addPreProcessor(PRE_PROCESSOR.BoundExt);
5741
+ parent.Dxf.addEventListener("cadChange", () => {
5742
+ this.trajectory && this.addPreProcessor(PRE_PROCESSOR$1.BoundExt);
5740
5743
  const lines = parent.Dxf.getLineSegments(true);
5741
5744
  this.set(lineDataToOriginalData(lines, parent.Dxf.originalZAverage), {
5742
5745
  trajectory: this.trajectory ?? void 0
@@ -5829,6 +5832,11 @@ class CorrectionDxf extends Component {
5829
5832
  originalData: this.originalData,
5830
5833
  data: this.data
5831
5834
  });
5835
+ let lines = this.getLineSegments(true);
5836
+ const doors = lines.filter((line) => line.userData.isDoor);
5837
+ lines = lines.filter((line) => !line.userData.isDoor);
5838
+ lines = buildDoubleWallGroup(lines, doors);
5839
+ lines.push(...doors);
5832
5840
  this.cad = new CAD().usePlugin(new DxfDataPlugin(this.getLineSegments(true))).usePlugin(new DxfDrawPlugin());
5833
5841
  const { angle, referenceLine } = CAD.obliquity(this.cad);
5834
5842
  this.cad.rotateAndResetOrigin(angle);
@@ -5910,457 +5918,407 @@ function closedPathArea(points) {
5910
5918
  }
5911
5919
  return Math.abs(area) / 2;
5912
5920
  }
5913
- const units = {
5914
- Unitless: 1,
5915
- // 无单位,1米 = 1(无单位)
5916
- Inches: 39.37007874015748,
5917
- // 英寸,1米 = 39.37007874015748英寸
5918
- Feet: 3.280839895013123,
5919
- // 英尺,1米 = 3.280839895013123英尺
5920
- Miles: 6213711922373339e-19,
5921
- // 英里,1米 = 0.0006213711922373339英里
5922
- Millimeters: 1e3,
5923
- // 毫米,1米 = 1000毫米
5924
- Centimeters: 100,
5925
- // 厘米,1米 = 100厘米
5926
- Meters: 1,
5927
- // 米,1米 = 1米
5928
- Kilometers: 1e-3,
5929
- // 千米,1米 = 0.001千米
5930
- Microinches: 3937007874015748e-8,
5931
- // 微英寸,1米 = 39370078.74015748微英寸
5932
- Mils: 39370.07874015748,
5933
- // 密耳,1米 = 39370.07874015748密耳
5934
- Yards: 1.0936132983377078,
5935
- // 码,1米 = 1.0936132983377078码
5936
- Angstroms: 1e10,
5937
- // 埃,1米 = 10^10埃
5938
- Nanometers: 1e9,
5939
- // 纳米,1米 = 10^9纳米
5940
- Microns: 1e6,
5941
- // 微米,1米 = 10^6微米
5942
- Decimeters: 10,
5943
- // 分米,1米 = 10分米
5944
- Decameters: 0.1,
5945
- // 十米,1米 = 0.1十米
5946
- Hectometers: 0.01,
5947
- // 百米,1米 = 0.01百米
5948
- Gigameters: 1e-9,
5949
- // 吉米,1米 = 10^-9吉米
5950
- "Astronomical units": 6684587122268445e-27,
5951
- // 天文单位,1米 = 0.000000000006684587122268445天文单位
5952
- "Light years": 10570008340246154e-32,
5953
- // 光年,1米 ≈ 0.00000000000000010570008340246154光年
5954
- Parsecs: 3240779289666404e-32
5955
- // 秒差距,1米 ≈ 0.00000000000000003240779289666404秒差距
5956
- };
5957
- function pathToLines(path) {
5958
- const lineSegments = [];
5959
- for (let i = 0; i < path.length; i++) {
5960
- lineSegments.push(new LineSegment(
5961
- path[i].clone(),
5962
- path[(i + 1) % path.length].clone()
5963
- ));
5964
- }
5965
- return lineSegments;
5966
- }
5967
- function linesToPath(lineSegments) {
5968
- return lineSegments.flatMap((line, index2) => {
5969
- if (index2 === lineSegments.length - 1) [...line.points, lineSegments[0].points[0]];
5970
- return [line.points[0]];
5971
- });
5972
- }
5973
- class Dxf extends Component {
5974
- static name = "Dxf";
5975
- shortLine = 0.04;
5976
- width = 0.04;
5977
- scale = 1;
5978
- originalData = [];
5979
- data = [];
5980
- originalBox = new Box2(0, 0, 0, 0);
5981
- box = new Box2(0, 0, 0, 0);
5982
- pointsGroups = [];
5983
- wallsGroup = [];
5984
- doors = [];
5985
- doorLineSegment = [];
5986
- verticalReferenceLine;
5987
- lineSegments = [];
5988
- originalZAverage = 0;
5989
- static EndType = {
5990
- etOpenSquare: 0,
5991
- etOpenRound: 1,
5992
- etOpenButt: 2
5993
- // etClosedLine: 3,
5994
- // etClosedPolygon: 4
5995
- };
5996
- static JoinType = {
5997
- jtSquare: 0,
5998
- jtRound: 1,
5999
- jtMiter: 2
6000
- };
6001
- /** 原始数据组
6002
- */
6003
- get lines() {
6004
- return this.lineSegments;
6005
- }
6006
- /**初始化
6007
- * @param width 墙体宽度
6008
- * @param scale 缩放比例
6009
- */
6010
- constructor(width = DEFAULT_WALL_WIDTH, scale = 1) {
6011
- super();
6012
- this.width = width;
6013
- this.scale = scale;
6014
- this.shortLine = width * 0.4;
6015
- }
6016
- /** 预处理数据
6017
- * @param data
6018
- */
6019
- preprocessing(data) {
6020
- let { lineSegments, verticalReferenceIndex, originalZAverage } = originalDataToLineData(data);
6021
- this.originalZAverage = originalZAverage;
6022
- if (verticalReferenceIndex === -1) {
6023
- const line = findVerticalReference(lineSegments);
6024
- verticalReferenceIndex = lineSegments.indexOf(line);
6025
- }
6026
- const verticalReferenceLine = lineSegments[verticalReferenceIndex];
6027
- verticalReferenceLine.userData.isVerticalReferenceLine = true;
6028
- data[verticalReferenceIndex].isVerticalReferenceLine = true;
6029
- this.verticalReferenceLine = verticalReferenceLine;
6030
- this.dispatchEvent({
6031
- type: "preprocessing",
6032
- data,
6033
- setData(originalDataItem) {
6034
- lineSegments = originalDataItem.map(({ start, end, ...opt }) => {
6035
- const lineSegment = new LineSegment(Point.from(start), Point.from(end));
6036
- lineSegment.userData = opt;
6037
- return lineSegment;
6038
- });
6039
- data = originalDataItem;
6040
- }
6041
- });
6042
- return { lineSegments, data };
6043
- }
6044
- /** 设置
6045
- * @param data 房屋结构数据,node环境可以为路径
6046
- * @param width 墙体宽度
6047
- * @param scale 缩放比例
6048
- * @param axisAlignmentCorrection 需要执行轴对称垂直纠正
6049
- * @param option
6050
- * @returns
6051
- */
6052
- async set(data, width = this.width, scale = this.scale) {
6053
- if (typeof data === "string") {
6054
- if (typeof global !== "undefined") {
6055
- const packageName = "fs";
6056
- const { default: fs2 } = await import(
6057
- /* @vite-ignore */
6058
- packageName
6059
- );
6060
- const buffer = fs2.readFileSync(data);
6061
- const json = JSON.parse(buffer.toString("utf-8"));
6062
- return this.set(json, width, scale);
5921
+ function findRingEdges(lines) {
5922
+ const lineUG = new LineSegmentUndirectedGraph(lines), unionFindSet = new UnionFindSet(lineUG.size), visited = /* @__PURE__ */ new Set(), ringEdges = [];
5923
+ function dfs(n1) {
5924
+ visited.add(n1);
5925
+ const neighbors = lineUG.getNeighbors(n1);
5926
+ neighbors?.forEach((n2) => {
5927
+ if (visited.has(n2)) return;
5928
+ if (unionFindSet.find(n1) === unionFindSet.find(n2)) {
5929
+ const line = lineUG.getLine(n1, n2);
5930
+ ringEdges.push(line);
6063
5931
  } else {
6064
- throw new Error("非node环境不允许使用路径");
5932
+ unionFindSet.union(n1, n2);
6065
5933
  }
6066
- }
6067
- if (data.length === 0) return;
6068
- this.scale = scale;
6069
- this.width = width;
6070
- this.doorLineSegment.length = 0;
6071
- if (data.length === 0) {
6072
- this.lineSegments = [];
6073
- this.originalData = data;
6074
- } else {
6075
- const res = this.preprocessing(data);
6076
- data = res.data;
6077
- this.lineSegments = res.lineSegments;
6078
- this.originalData = data;
6079
- }
6080
- const zList = [];
6081
- this.data = data.map(({ start, end, insetionArr, isDoor = false }, index2) => {
6082
- zList.push(start.z ?? 0, end.z ?? 0);
6083
- const lineSegment = this.lineSegments[index2];
6084
- return [
6085
- lineSegment.points[0],
6086
- lineSegment.points[1],
6087
- (insetionArr ?? []).map((i) => i.index),
6088
- isDoor,
6089
- index2
6090
- ];
6091
- });
6092
- this.originalZAverage = zList.reduce((count, num) => count + num, 0) / zList.length;
6093
- this.computedOriginalSize(data, this.originalBox);
6094
- this.dispatchEvent({
6095
- type: "setDta",
6096
- originalData: this.originalData,
6097
- data: this.data
6098
- });
6099
- this.createGroups();
6100
- this.computedSize();
6101
- this.dispatchEvent({
6102
- type: "createGroup",
6103
- groups: this.pointsGroups
6104
5934
  });
6105
5935
  }
6106
- /** 创建分组
6107
- * @description 根据相交数组insetionArr, 把相交的线段,划分到一组内,方便后续路径查找合并使用
6108
- * @returns
6109
- */
6110
- createGroups() {
6111
- const groups = [], visited = /* @__PURE__ */ new Set(), doorSet = /* @__PURE__ */ new Set(), doorVisitedRecord = /* @__PURE__ */ new Map();
6112
- const dfs = (index2, group2, preIndex = -1) => {
6113
- if (!this.data[index2]) return;
6114
- const [start, end, insetionArr, isDoor] = this.data[index2];
6115
- if (this.lineSegments[index2]?.userData.isBayWindow) return;
6116
- visited.add(index2);
6117
- if (isDoor) {
6118
- if (!doorVisitedRecord.has(index2)) doorVisitedRecord.set(index2, []);
6119
- doorVisitedRecord.get(index2)?.push(preIndex);
6120
- return doorSet.add(this.data[index2]);
6121
- }
6122
- group2.push([start, end]);
6123
- insetionArr.forEach((i) => {
6124
- if (!visited.has(i)) {
6125
- dfs(i, group2, index2);
6126
- }
6127
- });
6128
- };
6129
- this.data.forEach((_, index2) => {
6130
- if (this.lineSegments[index2]?.userData.isBayWindow) return;
6131
- if (!visited.has(index2)) {
6132
- const group2 = [];
6133
- dfs(index2, group2);
6134
- groups.push(group2);
6135
- }
6136
- });
6137
- this.doors = [...doorSet];
6138
- this.pointsGroups = groups;
6139
- return groups;
5936
+ lineUG.forEach((n1) => {
5937
+ if (visited.has(n1)) return;
5938
+ dfs(n1);
5939
+ });
5940
+ return ringEdges;
5941
+ }
5942
+ function findMinRing(lines, ringEdges) {
5943
+ const grid = createPointVirtualGrid(lines), temLine = new LineSegment(), queryLine = new LineSegment(), rings = [], visited = /* @__PURE__ */ new Set();
5944
+ function getDirect(line, preDirect) {
5945
+ const center = line.center, direct = line.normal();
5946
+ temLine.start.copy(center);
5947
+ temLine.end.copy(center).add(direct.clone().multiplyScalar(1));
5948
+ const point = preDirect.getIntersection(temLine);
5949
+ temLine.end.copy(point);
5950
+ return temLine.clone();
6140
5951
  }
6141
- /** 计算当前墙体数据的边界框
6142
- * @description 根据分组数据pointsGroups,计算包围盒, pointsGroups数据为缩放后数据。
6143
- * @description 可通过box属性查看计算结果。
6144
- * @returns
6145
- */
6146
- computedSize() {
6147
- const xArr = this.pointsGroups.flatMap((points) => points.flatMap((p) => [p[0].x, p[1].x]));
6148
- const yArr = this.pointsGroups.flatMap((points) => points.flatMap((p) => [p[0].y, p[1].y]));
6149
- const minX = Math.min(...xArr);
6150
- const minY = Math.min(...yArr);
6151
- const maxX = Math.max(...xArr);
6152
- const maxY = Math.max(...yArr);
6153
- this.box.set(minX, minY, maxX, maxY);
6154
- return this.box;
6155
- }
6156
- /** 线路拓扑
6157
- * @description 处理线路拓扑,使线路有序链接,形成长路径
6158
- * @param lines
6159
- */
6160
- lineTopology(lines) {
6161
- const visited = [];
6162
- function dfs(index2, linePath) {
6163
- const [_0, a2] = lines[index2];
6164
- visited[index2] = true;
6165
- linePath.push(a2);
6166
- for (let i = 0; i < lines.length; i++) {
6167
- const [b1, _1] = lines[i];
6168
- if (!visited[i]) {
6169
- if (Math.abs(a2.x - b1.x) < 1e-6 && Math.abs(a2.y - b1.y) < 1e-6) {
6170
- return dfs(i, linePath);
6171
- }
6172
- }
6173
- }
6174
- }
6175
- const linePaths = [];
6176
- for (let i = 0; i < lines.length; i++) {
6177
- if (!visited[i]) {
6178
- const linePath = [lines[i][0]];
6179
- dfs(i, linePath);
6180
- linePaths.push(linePath);
5952
+ function find(edge, line, point, preLine, preDirect, path, isPreDirect = true) {
5953
+ if (path.length > lines.length) return;
5954
+ if (visited.has(line)) return;
5955
+ visited.add(line);
5956
+ let direct;
5957
+ const otherPoint = line.getAnotherPoint(point);
5958
+ if (isPreDirect) {
5959
+ if (line.vertical(preLine)) direct = getDirect(line, preDirect);
5960
+ else {
5961
+ const center = line.center, x = center.distance(preDirect.start), d0 = center.direction(preDirect.start);
5962
+ direct = preDirect.directionMove(d0, x);
6181
5963
  }
6182
- }
6183
- return linePaths;
6184
- }
6185
- /** 合并方向相同的线段
6186
- * @param lines
6187
- * @param errAngle
6188
- */
6189
- mergeSameDirectionLine(lines, errAngle = 3) {
6190
- if (lines[0].includedAngle(lines[lines.length - 1]) < 0.1) {
6191
- const line = lines.pop();
6192
- line.end.copy(lines[0].end);
6193
- lines.splice(0, 1, line);
6194
- }
6195
- const filterLines = [lines[0]];
6196
- for (let i = 1; i < lines.length; i++) {
6197
- const line = lines[i];
6198
- const preLine = lines[i - 1];
6199
- if (preLine.includedAngle(line) < errAngle) {
6200
- preLine.end.copy(line.end);
6201
- } else {
6202
- filterLines.push(line);
5964
+ } else direct = preDirect;
5965
+ let result = grid.queryPoint(otherPoint, true);
5966
+ if (result.length === 1) {
5967
+ const item = result[0], nextLine = item.userData, otherPoint1 = nextLine.getAnotherPoint(item.point), d0 = otherPoint1.direction(item.point);
5968
+ if (edge === nextLine) return path;
5969
+ if (nextLine.vertical(line)) {
5970
+ const d1 = otherPoint.direction(point), center = nextLine.center, isSameDirection = d0.angleBetween(direct.direction()) < 1e-9;
5971
+ direct.start.copy(center);
5972
+ direct.end.copy(center).add(d1.multiplyScalar(0.02));
5973
+ if (isSameDirection) direct.end.rotate(direct.start, Math.PI);
5974
+ path.push(nextLine);
5975
+ if (find(edge, nextLine, item.point, line, direct, path, false)) return path;
6203
5976
  }
6204
- }
6205
- return filterLines;
6206
- }
6207
- /** etOpenRound 去除毛刺
6208
- * @description 检查连续的短线段数量,去除合并后产生的毛刺
6209
- */
6210
- squareRemoveBurr(path) {
6211
- if (path.length < 3) return path;
6212
- const filterLines = [path[0]];
6213
- for (let i = 1; i < path.length; i++) {
6214
- const prev = path[i - 1];
6215
- const curr = path[i];
6216
- const len = prev.distance(curr);
6217
- if (len < this.width * 0.5) {
6218
- let count = 0;
6219
- for (let j = i + 1; j < path.length; j++) {
6220
- const prev2 = path[j - 1];
6221
- const curr2 = path[j];
6222
- const len2 = prev2.distance(curr2);
6223
- if (len2 < this.width * 0.8) count++;
6224
- else break;
6225
- }
6226
- if (count === 0 && i + count === path.length - 1) ;
6227
- else if (i == 1 && count === 1) ;
6228
- else if (count === 3) {
6229
- filterLines.push(path[i + 1]);
6230
- i += count;
6231
- } else if (count === 5) {
6232
- filterLines.push(path[i + 2]);
6233
- i += count;
6234
- } else {
6235
- filterLines.push(curr);
5977
+ } else {
5978
+ queryLine.start.copy(direct.start).add(direct.direction().multiplyScalar(5e-3));
5979
+ const lines2 = [line, ...result.map((item) => {
5980
+ const line2 = item.userData;
5981
+ line2.length();
5982
+ return line2;
5983
+ })];
5984
+ for (let i = 0; i < result.length; i++) {
5985
+ const item = result[i], nextLine = item.userData;
5986
+ if (edge === nextLine) return path;
5987
+ if (result.length === 4 && nextLine.parallel(line)) continue;
5988
+ queryLine.end.copy(nextLine.center);
5989
+ if (!lines2.some((line1) => {
5990
+ if (line1 === nextLine) return false;
5991
+ return line1.intersectLineSegment(queryLine);
5992
+ })) {
5993
+ path.push(nextLine);
5994
+ return find(edge, nextLine, item.point, line, direct, path);
6236
5995
  }
6237
- } else {
6238
- filterLines.push(curr);
6239
5996
  }
6240
5997
  }
6241
- return filterLines;
6242
5998
  }
6243
- /**
6244
- * 线段矫直, 线段中心突刺
6245
- * @description 突变长度小于墙体宽度,该线段可能为突起线段,
6246
- * @description 判断后续第2线段与上一条线段是否方向相同,相同就为突刺
6247
- */
6248
- lineSegmentStraightening(path) {
6249
- for (let i = 0; i < path.length; i++) {
6250
- const p1 = path[i];
6251
- const p2 = path[(i + 1) % path.length];
6252
- if (p1.distance(p2) > this.shortLine) {
6253
- path.push(...path.slice(0, i + 1));
6254
- path.splice(0, i + 1);
6255
- break;
6256
- }
6257
- }
6258
- const lines = this.mergeSameDirectionLine(pathToLines(path)), filterLines = [lines[0]];
6259
- for (let i = 1; i < lines.length; i++) {
6260
- const line = lines[i];
6261
- const preLine = lines[(lines.length + i - 1) % lines.length];
6262
- if (line.length() > this.width * 0.9) {
6263
- filterLines.push(line);
6264
- continue;
5999
+ for (let i = 0; i < ringEdges.length; i++) {
6000
+ const line = ringEdges[i], center = line.center, startList = grid.queryPoint(line.start, true), endList = grid.queryPoint(line.end, true), minList = startList.length < endList.length ? startList : endList, directList = [], list = minList.filter((item) => {
6001
+ if (minList.length === 3 && item.userData?.parallel(line)) return false;
6002
+ const otherPoint = item.userData?.getAnotherPoint(item.point);
6003
+ directList.push(otherPoint.direction(item.point));
6004
+ return true;
6005
+ });
6006
+ const ringList = [];
6007
+ for (let i2 = 0; i2 < list.length; i2++) {
6008
+ const item = list[i2];
6009
+ let direct = directList[i2];
6010
+ if (item.userData?.parallel(line)) {
6011
+ const otherIndex = i2 ? 0 : 1, other = directList[otherIndex];
6012
+ direct = other.clone().multiplyScalar(-1);
6265
6013
  }
6266
- const line1 = lines[i + 1];
6267
- if (line1 && line1.length() > this.width * 0.9) {
6268
- filterLines.push(line);
6269
- filterLines.push(line1);
6270
- i = i + 1;
6271
- continue;
6014
+ visited.clear();
6015
+ const directLine = new LineSegment(center.clone(), center.clone().add(direct.clone().multiplyScalar(0.2)));
6016
+ const path = find(line, item.userData, item.point, line, directLine, [line, item.userData]);
6017
+ if (path) {
6018
+ ringList.push([...path]);
6272
6019
  }
6273
- const line2 = lines[i + 2];
6274
- if (line2 && preLine.includedAngle(line2) < 2) {
6275
- i = i + 2;
6276
- filterLines.push(line2);
6277
- } else filterLines.push(line);
6278
6020
  }
6279
- return filterLines.length > 3 ? linesToPath(this.mergeSameDirectionLine(filterLines)) : [];
6280
- }
6281
- /**
6282
- * 移除短线段
6283
- * @todo 根据线段两端线段长度,选取参照物_|▔▔
6284
- * @param path
6285
- */
6286
- removeShortLine(path, shortLine = this.shortLine) {
6287
- const lines = pathToLines(path), filterLines = [], PI_12 = Math.PI / 180;
6288
- for (let i = 0; i < lines.length; i++) {
6289
- const line = lines[i], len = line.length(), currentIndex = i;
6290
- if (len > shortLine || filterLines.length === 0) {
6291
- filterLines.push(line);
6292
- continue;
6293
- }
6294
- let nextline = lines[++i];
6295
- const preLine = filterLines[filterLines.length - 1], d1 = preLine.direction();
6296
- while (i < lines.length) {
6297
- const angle = d1.angleBetween(nextline.direction()) / PI_12;
6298
- if (nextline.length() <= shortLine || angle < 4 || angle > 180 - 4) {
6299
- nextline = lines[++i];
6300
- } else break;
6301
- }
6302
- if (!nextline) continue;
6303
- const targetLine = lines[i - 1];
6304
- if (preLine.length() > targetLine.length()) {
6305
- const intersectPoint = preLine.getIntersection(nextline);
6306
- if (intersectPoint) {
6307
- const p0 = preLine.points[1].clone(), p1 = nextline.points[0].clone();
6308
- preLine.points[1].copy(intersectPoint);
6309
- nextline.points[0].copy(intersectPoint);
6310
- if (preLine.length() < this.width) {
6311
- preLine.points[1].copy(p0);
6312
- nextline.points[0].copy(p0);
6313
- } else if (nextline.length() < this.width) {
6314
- preLine.points[1].copy(p1);
6315
- nextline.points[0].copy(p1);
6316
- }
6317
- } else {
6318
- preLine.points[1].copy(nextline.points[0]);
6319
- }
6320
- filterLines.push(nextline);
6321
- } else {
6322
- i = currentIndex;
6323
- }
6021
+ if (ringList.length === 2) {
6022
+ const set2 = new Set(ringList[0]);
6023
+ const count = ringList[1].filter((line2) => set2.has(line2)).length;
6024
+ if (count > 1) ringList.splice(0, 2, ringList[0].length < ringList[1].length ? ringList[0] : ringList[1]);
6324
6025
  }
6325
- return filterLines.length > 3 ? linesToPath(filterLines) : [];
6026
+ rings.push(...ringList);
6326
6027
  }
6327
- /** 线偏移
6328
- * @description 使用 ClipperLib 对每个点组进行线偏移处理,生成具有指定宽度的墙体路径
6329
- */
6330
- lineOffset(endType = Dxf.EndType.etOpenSquare, joinType = Dxf.JoinType.jtMiter, scale = 1e4) {
6331
- let solutions = new ClipperLib.Paths();
6332
- const offset = new ClipperLib.ClipperOffset(20, 0.25);
6333
- this.pointsGroups.forEach((points) => {
6334
- const linePaths = this.lineTopology(points).map((linePath) => linePath.map((p) => p.clone().mutiplyScalar(scale)));
6335
- offset.AddPaths(linePaths, joinType, endType);
6336
- });
6337
- offset.Execute(solutions, this.width / 2 * scale);
6338
- this.wallsGroup = solutions.map((ps2) => {
6339
- let path = ps2.map((p) => Point.from(p).divisionScalar(scale));
6340
- path = this.lineSegmentStraightening(path);
6341
- if (endType == Dxf.EndType.etOpenSquare) path = this.squareRemoveBurr(path);
6342
- return path;
6343
- }).filter((points) => points.length > 4 || closedPathArea(points) > 0.2);
6344
- this.dispatchEvent({
6345
- type: "lineOffset",
6346
- wallsGroup: this.wallsGroup
6028
+ return rings;
6029
+ }
6030
+ function ringsDeduplication(rings) {
6031
+ const map = new ArrayMap(), newRings = [];
6032
+ rings.forEach((ring) => map.append(ring.length, ring));
6033
+ for (const rings2 of map.values()) {
6034
+ const lineIndexGenerator = new LineIndexGenerator(rings2.flat());
6035
+ const ringMap = new ArrayMap();
6036
+ rings2.map((ring) => {
6037
+ const key = ring.map((line) => lineIndexGenerator.getIndex(line)).sort((a, b) => a - b).join(",");
6038
+ ringMap.append(key, ring);
6347
6039
  });
6348
- return this.wallsGroup;
6040
+ ringMap.forEach((list) => newRings.push(list[0]));
6349
6041
  }
6350
- /** 垂直纠正
6351
- * @param option
6352
- */
6353
- async axisAlignCorr(option) {
6354
- if (this.verticalReferenceLine) {
6355
- const lines = this.getLineSegments();
6042
+ return newRings;
6043
+ }
6044
+ function findMaxRing(lines, ringEdges) {
6045
+ const rings = findMinRing(lines, ringEdges), visited = /* @__PURE__ */ new Map(), unionFindSet = new UnionFindSet(rings.length), internalEdges = [];
6046
+ rings.forEach((ring, index2) => {
6047
+ for (let i = 0; i < ring.length; i++) {
6048
+ const line = ring[i];
6049
+ if (visited.has(line)) unionFindSet.union(index2, visited.get(line));
6050
+ else visited.set(line, index2);
6051
+ }
6052
+ });
6053
+ const countMap = new CountMap();
6054
+ const groups = unionFindSet.getAllSets().valueArray.map((list) => {
6055
+ let group2 = list.map((i) => rings[i]);
6056
+ if (group2.length === 1) return group2[0];
6057
+ countMap.clear();
6058
+ group2 = ringsDeduplication(group2);
6059
+ group2.forEach((lines2) => lines2.forEach((line) => countMap.set(line)));
6060
+ const ring = countMap.reduce((lines2, count, line) => {
6061
+ if (count === 1) lines2.push(line);
6062
+ else internalEdges.push(line);
6063
+ return lines2;
6064
+ }, []);
6065
+ return ring;
6066
+ });
6067
+ return {
6068
+ internalEdges,
6069
+ rings: groups
6070
+ };
6071
+ }
6072
+ function findClosedPolygons(lines) {
6073
+ const linesGroup = LineSegment.groupByPath(lines), rings = [], internalEdges = [];
6074
+ linesGroup.forEach((lines2, _) => {
6075
+ try {
6076
+ let ringEdges = findRingEdges(lines2);
6077
+ const result = findMaxRing(lines2, ringEdges);
6078
+ rings.push(...result.rings);
6079
+ internalEdges.push(...result.internalEdges);
6080
+ } catch (error) {
6081
+ console.warn("环查找出现异常:", error.message);
6082
+ }
6083
+ });
6084
+ const internalEdgesSet = /* @__PURE__ */ new Set([...internalEdges]);
6085
+ const groupsSet = /* @__PURE__ */ new Set([...rings.flat()]);
6086
+ lines = lines.filter((line) => !internalEdgesSet.has(line));
6087
+ lines.forEach((line) => !groupsSet.has(line));
6088
+ lines = [...new Set(lines)];
6089
+ return {
6090
+ newLines: lines,
6091
+ rings,
6092
+ internalEdges
6093
+ };
6094
+ }
6095
+ function createGroup(lineSegments) {
6096
+ const doors = [];
6097
+ lineSegments = lineSegments.filter((line) => {
6098
+ if (line.userData.isDoor) {
6099
+ doors.push(line);
6100
+ return false;
6101
+ }
6102
+ return true;
6103
+ });
6104
+ lineSegments = buildDoubleWallGroup(lineSegments);
6105
+ lineSegments = DoubleWallHelper.complementSide(lineSegments);
6106
+ lineSegments = buildDoubleWallGroup(lineSegments, doors, true, true);
6107
+ return [...lineSegments, ...doors];
6108
+ }
6109
+ function getGroups(lines, updateGroup = true) {
6110
+ if (updateGroup) lines = createGroup(lines);
6111
+ const map = lines.reduce((map2, line) => {
6112
+ const groups = LineGroupType.get(line);
6113
+ groups.forEach((group2) => {
6114
+ let id = group2.id ?? "wall";
6115
+ if (group2.type === "wall") id = "wall";
6116
+ map2.append(id, line);
6117
+ });
6118
+ return map2;
6119
+ }, new ArrayMap());
6120
+ return map;
6121
+ }
6122
+ function handleGroup(map) {
6123
+ const group2 = map.group((lines) => {
6124
+ if (LineGroupType.everyType(lines, "bayWindow")) return "wall";
6125
+ if (LineGroupType.everyType(lines, "doubleWall")) return "doubleWall";
6126
+ return "wall";
6127
+ }), walls = group2.get("wall") ?? [], doubleWalls = group2.get("doubleWall") ?? [];
6128
+ let newlines = [], doubleWallsSet = new Set(doubleWalls.flat());
6129
+ walls.forEach((lines) => {
6130
+ lines.forEach((line) => doubleWallsSet.has(line) || newlines.push(line));
6131
+ });
6132
+ doubleWalls.forEach((lines) => {
6133
+ lines = [...new Set(lines)];
6134
+ const groups = clippingDoubleWall(lines);
6135
+ groups.forEach((lines2) => {
6136
+ if (lines2.length < 4) return newlines.push(...lines2);
6137
+ lines2 = lines2.sort((a, b) => a.length() - b.length());
6138
+ const line1 = lines2[0], line2 = lines2[1];
6139
+ const newLine = new LineSegment(line1.center.clone(), line2.center.clone());
6140
+ mergeLineUserData(newLine, lines2);
6141
+ newLine.userData.wallWidth = line1.length();
6142
+ newlines.push(newLine);
6143
+ mergeWindow(newLine);
6144
+ });
6145
+ });
6146
+ newlines = newlines.map((line) => line.clone());
6147
+ return newlines.filter((line) => !line.userData.isBayWindow);
6148
+ }
6149
+ function toJson(lineSegments, name = "测试", communityName = "") {
6150
+ const scale = 1, idMap = /* @__PURE__ */ new Map();
6151
+ let index2 = 0;
6152
+ return {
6153
+ version: "2",
6154
+ name,
6155
+ communityName,
6156
+ city: "",
6157
+ province: "",
6158
+ height: 2.8 * scale,
6159
+ walls: lineSegments.map((line) => {
6160
+ if (line.userData.isDoor && !line.userData.doorDirectConnection) return;
6161
+ idMap.set(line, index2);
6162
+ return {
6163
+ ID: index2++,
6164
+ start: { x: line.start.x * scale, y: line.start.y * scale },
6165
+ end: { x: line.end.x * scale, y: line.end.y * scale },
6166
+ thickness: (line.userData.wallWidth ? line.userData.wallWidth : 0.12) * scale,
6167
+ type: "LINE",
6168
+ isDoor: line.userData.isDoor,
6169
+ loadBearingWall: false,
6170
+ height: 2.8 * scale
6171
+ };
6172
+ }).filter((i) => !!i),
6173
+ pillars: [],
6174
+ beams: [],
6175
+ holes: lineSegments.flatMap((line) => {
6176
+ if (line.userData.isDoor && line.userData.doorDirectConnection) {
6177
+ return {
6178
+ id: index2++,
6179
+ type: "DOOR",
6180
+ openSide: "RIGHT",
6181
+ start: {
6182
+ x: line.start.x * scale,
6183
+ y: line.start.y * scale
6184
+ },
6185
+ end: {
6186
+ x: line.end.x * scale,
6187
+ y: line.end.y * scale
6188
+ },
6189
+ height: 2.1 * scale,
6190
+ qroundClearance: 0 * scale,
6191
+ sillHeight: (line.userData?.sillHeight ?? 0) * scale
6192
+ };
6193
+ } else if (line.userData.isWindow && line.userData.drawWindow) {
6194
+ return line.userData.drawWindow.map((item) => {
6195
+ const center = Point.from(item.p);
6196
+ const start = center.clone().add(
6197
+ line.direction().multiplyScalar(item.width * 0.5)
6198
+ );
6199
+ const end = center.clone().add(
6200
+ line.direction().multiplyScalar(-item.width * 0.5)
6201
+ );
6202
+ return {
6203
+ id: index2++,
6204
+ type: "WINDOW",
6205
+ start: {
6206
+ x: start.x * scale,
6207
+ y: start.y * scale
6208
+ },
6209
+ end: {
6210
+ x: end.x * scale,
6211
+ y: end.y * scale
6212
+ },
6213
+ height: 1.6 * scale,
6214
+ groundClearance: (line.userData?.groundClearance ?? 0.9) * scale,
6215
+ sillHeight: (line.userData?.groundClearance ?? 0.9) * scale
6216
+ };
6217
+ });
6218
+ }
6219
+ }).filter((i) => !!i),
6220
+ rooms: []
6221
+ };
6222
+ }
6223
+ function lineDataToThreeVJiaJson(lineSegments, angle = 0, updateGroup = true) {
6224
+ const group2 = getGroups(lineSegments, updateGroup);
6225
+ let newLines = handleGroup(group2);
6226
+ newLines = LineSegmentUndirectedGraph.rotate(newLines, angle, (line, center, angle2) => {
6227
+ if (line.userData.drawWindow) {
6228
+ line.userData.drawWindow.forEach((windowItem) => {
6229
+ const point = Point.from(windowItem.p);
6230
+ point.rotate(center, angle2 * (Math.PI / 180));
6231
+ windowItem.p = point.toJson(windowItem.p.z);
6232
+ });
6233
+ }
6234
+ });
6235
+ return {
6236
+ lines: newLines,
6237
+ toJson(name = "测试", communityName = "") {
6238
+ return toJson(newLines, name, communityName);
6239
+ }
6240
+ };
6241
+ }
6242
+ const PRE_PROCESSOR = {
6243
+ DoorsAnalysis(lines) {
6244
+ return lines;
6245
+ },
6246
+ AxisAlignCorr(lines, option) {
6247
+ const verticalReferenceLine = findVerticalReference(lines);
6248
+ if (verticalReferenceLine) {
6356
6249
  const t = performance.now();
6357
- const lineSegments = AxisAlignCorr.correction(lines, this.verticalReferenceLine, option);
6250
+ const lineSegments = AxisAlignCorr.correction(lines, verticalReferenceLine, option);
6358
6251
  console.log("垂直纠正消耗时间:", (performance.now() - t).toFixed(2), "ms", "处理线段数量:", lines.length);
6359
- const data = lineDataToOriginalData(lineSegments, this.originalZAverage);
6360
- await this.set(data);
6252
+ return lineSegments;
6361
6253
  } else {
6362
- throw new Error("未找到一条垂直纠正基准轴线");
6254
+ console.error("未找到一条垂直纠正基准轴线, 未做垂直纠正处理");
6255
+ }
6256
+ return lines;
6257
+ },
6258
+ BoundExt(lines, { trajectory, onBoundExt }) {
6259
+ if (!trajectory) {
6260
+ console.error("没有传入轨迹文件");
6261
+ return lines;
6363
6262
  }
6263
+ let t = performance.now();
6264
+ lines = BoundExt.boundExtbyTraj({ lines, trajectory, wallWidth: DEFAULT_WALL_WIDTH, findCallBack: onBoundExt }).lines;
6265
+ console.log(`外墙外扩消耗时间: ${(performance.now() - t).toFixed(2)} ms 处理线段数量: ${lines.length}`);
6266
+ return lines;
6267
+ },
6268
+ AngleCorr(lines) {
6269
+ const box = Box2.fromByLineSegment(...lines), center = box.center, vrLine = findVerticalReference(lines), angle = -vrLine.direction().angleBetween(new Point(0, 1), "angle", "360") * (Math.PI / 180);
6270
+ lines.forEach((line) => {
6271
+ line.rotate(angle, center);
6272
+ if (line.userData.isWindow) {
6273
+ line.userData.drawWindow?.forEach((d) => {
6274
+ const p = Point.from(d.p);
6275
+ p.rotate(center, angle);
6276
+ d.p = p.toJson(d.p.z);
6277
+ });
6278
+ }
6279
+ });
6280
+ return lines;
6281
+ },
6282
+ ResetGeometricCenter(lines) {
6283
+ const box = Box2.fromByLineSegment(...lines), center = box.center;
6284
+ lines.forEach((line) => {
6285
+ line.start.division(center);
6286
+ line.end.division(center);
6287
+ });
6288
+ return lines;
6289
+ }
6290
+ };
6291
+ class Dxf extends Component {
6292
+ static name = "Dxf";
6293
+ static PRE_PROCESSOR = PRE_PROCESSOR;
6294
+ width = 0.04;
6295
+ originalData = [];
6296
+ data = [];
6297
+ box = new Box2(0, 0, 0, 0);
6298
+ cad = null;
6299
+ lineSegments = [];
6300
+ doorLineSegment = [];
6301
+ verticalReferenceLine;
6302
+ originalZAverage = 0;
6303
+ _preProcessorSet = /* @__PURE__ */ new Set();
6304
+ trajectory = null;
6305
+ onBoundExt;
6306
+ /** 原始数据组
6307
+ */
6308
+ get lines() {
6309
+ return this.lineSegments;
6310
+ }
6311
+ /**初始化
6312
+ * @param width 墙体宽度
6313
+ */
6314
+ constructor(width = 0.04) {
6315
+ super();
6316
+ this.width = width;
6317
+ }
6318
+ setTrajectory(trajectory, onBoundExt) {
6319
+ this.trajectory = trajectory;
6320
+ this.onBoundExt = this.onBoundExt ?? onBoundExt;
6321
+ return this;
6364
6322
  }
6365
6323
  /** 完整线段数据
6366
6324
  * @returns
@@ -6380,253 +6338,110 @@ class Dxf extends Component {
6380
6338
  return lines;
6381
6339
  }
6382
6340
  /**
6383
- * 将点云结构转换为Float32Array
6384
- */
6385
- to3DArray(scale, z = this.originalZAverage) {
6386
- const array = [];
6387
- this.wallsGroup.forEach((points) => {
6388
- for (let i = 0; i < points.length; i++) {
6389
- const point1 = points[i];
6390
- const nextIndex = i === points.length - 1 ? 0 : i + 1;
6391
- const point2 = points[nextIndex];
6392
- array.push(point1.X * scale, point1.Y * scale, z, point2.X * scale, point2.Y * scale, z);
6393
- }
6394
- });
6395
- return new Float32Array(array);
6396
- }
6397
- /** 获取角度范围
6398
- * @param center
6399
- * @param p1
6400
- * @param p2
6401
- * @returns
6402
- */
6403
- getArcAngleRange(center, p1, p2) {
6404
- const x1 = p1.x - center.x;
6405
- const y1 = p1.y - center.y;
6406
- const x2 = p2.x - center.x;
6407
- const y2 = p2.y - center.y;
6408
- let angle1 = Math.atan2(y1, x1);
6409
- let angle2 = Math.atan2(y2, x2);
6410
- angle1 = angle1 < 0 ? angle1 + 2 * Math.PI : angle1;
6411
- angle2 = angle2 < 0 ? angle2 + 2 * Math.PI : angle2;
6412
- let r1, r2;
6413
- const diff = Math.abs(angle2 - angle1);
6414
- if (diff <= Math.PI) {
6415
- r1 = Math.min(angle1, angle2);
6416
- r2 = Math.max(angle1, angle2);
6417
- } else {
6418
- r1 = Math.max(angle1, angle2);
6419
- r2 = Math.min(angle1, angle2) + 2 * Math.PI;
6420
- }
6421
- return [r1 / (Math.PI / 180), r2 / (Math.PI / 180)];
6422
- }
6423
- /**
6424
- * 线段数据转为原始json数据
6425
- */
6426
- lineDataToOriginalData(lines, quadtree) {
6427
- return lineDataToOriginalData(lines, this.originalZAverage, quadtree);
6428
- }
6429
- /**
6430
- * 转为绘制数据
6431
- */
6432
- toDrawDataJson(unit = "Millimeters") {
6433
- const s = units[unit], data = {
6434
- unit,
6435
- line: [],
6436
- arc: [],
6437
- dimensionLine: [],
6438
- center: this.box.center.toJson(),
6439
- width: this.box.width * s,
6440
- height: this.box.height * s,
6441
- scale: s
6442
- };
6443
- let color = "white";
6444
- function drawLine(p1, p2) {
6445
- data.line.push([p1.x * s, p1.y * s, p2.x * s, p2.y * s, color]);
6446
- }
6447
- function drawArc(pos, radius, startAngle, endAngle) {
6448
- data.arc.push([
6449
- pos.x * s,
6450
- pos.y * s,
6451
- radius * s,
6452
- startAngle,
6453
- endAngle,
6454
- color
6455
- ]);
6456
- }
6457
- for (let i = 0; i < this.originalData.length; i++) {
6458
- const line = this.originalData[i];
6459
- if (line.isVerticalReferenceLine) {
6460
- data.dimensionLine.push([line.start.x * s, line.start.y * s, line.end.x * s, line.end.y * s]);
6461
- break;
6462
- }
6463
- }
6464
- this.wallsGroup.forEach((points) => {
6465
- for (let i = 0; i < points.length; i++) {
6466
- const point1 = points[i];
6467
- const nextIndex = i === points.length - 1 ? 0 : i + 1;
6468
- const point2 = points[nextIndex];
6469
- drawLine(point1, point2);
6470
- }
6471
- });
6472
- const doorThickness = this.width * 0.2;
6473
- const list = [];
6474
- this.doorLineSegment.forEach((lineSegment) => {
6475
- if (lineSegment.length() < 0.4) return;
6476
- const line = lineSegment.clone().expansion(-this.width * 0.5);
6477
- color = "cyan";
6478
- if (line.length() < 1.2) {
6479
- line.expansion(-doorThickness * 0.5);
6480
- const normal = lineSegment.normal();
6481
- let door = new LineSegment(
6482
- line.start.clone(),
6483
- line.start.clone().add(normal.clone().multiplyScalar(line.length()))
6484
- );
6485
- const box = door.clone().directionMove(door.normal(), line.length() * -0.5).expandToRectangle(line.length(), "bothSides");
6486
- for (let j = 0; j < list.length; j++) {
6487
- if (list[j].intersectRectangle(box)) {
6488
- door = new LineSegment(
6489
- line.start.clone(),
6490
- line.start.clone().add(normal.clone().multiplyScalar(-line.length()))
6491
- );
6492
- break;
6493
- }
6494
- }
6495
- door.expansion(-doorThickness * 0.5).expandToRectangle(this.width * 0.2, "bothSides").path2D((p1, p2) => drawLine(p1, p2));
6496
- const a = line.length(), b = door.length(), r = (a ** 2 + b ** 2) / (2 * b), center = door.end.clone().add(door.direction().multiplyScalar(-r)), [startAngle, endAngle] = this.getArcAngleRange(center, line.end, door.end);
6497
- drawArc(center, r, Math.min(startAngle, endAngle), Math.max(startAngle, endAngle));
6498
- list.push(box);
6499
- } else {
6500
- line.clone().expansion(-this.width * 0.5).expandToRectangle(this.width).path2D((p1, p2) => drawLine(p1, p2));
6501
- line.clone().directionMove(line.normal(), doorThickness * 0.5).directionMove(line.direction(), doorThickness * 0.5).expansion(-line.length() * 0.45, "end").forward(doorThickness * 0.5).expandToRectangle(doorThickness).path2D((p1, p2) => drawLine(p1, p2));
6502
- line.clone().directionMove(line.normal(), -doorThickness * 0.5).directionMove(line.direction(), -doorThickness * 0.5).expansion(-line.length() * 0.45, "start").forward(-doorThickness * 0.5).expandToRectangle(doorThickness).path2D((p1, p2) => drawLine(p1, p2));
6503
- }
6341
+ * 预处理数据
6342
+ * @param data
6343
+ */
6344
+ preprocessing(data, options) {
6345
+ let { lineSegments, originalZAverage } = originalDataToLineData(data);
6346
+ this.originalZAverage = originalZAverage;
6347
+ lineSegments.forEach((line) => {
6348
+ if (line.userData.isDoor && line.userData.doorDirectConnection) this.doorLineSegment.push(line);
6504
6349
  });
6505
- color = "yellow";
6506
- this.lineSegments.forEach((line) => {
6507
- if (!line.userData.isWindow) return false;
6508
- if (Array.isArray(line.userData.drawWindow)) {
6509
- line.userData.drawWindow.forEach((w) => {
6510
- const { p, width } = w;
6511
- const center = Point.from(p);
6512
- const start = center.clone().add(line.direction().multiplyScalar(width * 0.5));
6513
- const end = center.clone().add(line.direction().multiplyScalar(-width * 0.5));
6514
- const blinds = new LineSegment(start, end);
6515
- drawLine(blinds.start, blinds.end);
6516
- blinds.expandToRectangle(this.width, "bothSides").path2D((p1, p2) => drawLine(p1, p2));
6517
- });
6518
- }
6350
+ this._preProcessorSet.forEach((handler) => {
6351
+ lineSegments = handler(lineSegments, options);
6519
6352
  });
6520
- return data;
6353
+ this.verticalReferenceLine = findVerticalReference(lineSegments);
6354
+ this.verticalReferenceLine.userData.isVerticalReferenceLine = true;
6355
+ return { lineSegments, data };
6521
6356
  }
6522
- /**
6523
- * @param type
6357
+ /** 添加预处理
6358
+ * @param handler
6524
6359
  */
6525
- async toDxfImageBlob(unit = "Centimeters", type = "image/jpeg", background = "#000") {
6526
- const data = this.toDrawDataJson(unit);
6527
- let canvas;
6528
- if (typeof window !== "undefined") {
6529
- canvas = document.createElement("canvas");
6530
- } else if (typeof global !== "undefined") {
6531
- const { createCanvas } = await include("canvas");
6532
- canvas = createCanvas();
6533
- } else {
6534
- throw new Error("创建画布失败");
6360
+ addPreProcessor(handler) {
6361
+ this._preProcessorSet.add(handler);
6362
+ return this;
6363
+ }
6364
+ /** 删除预处理
6365
+ * @param handler
6366
+ */
6367
+ removePreProcessor(handler) {
6368
+ this._preProcessorSet.delete(handler);
6369
+ return this;
6370
+ }
6371
+ /** 清空预处理
6372
+ * @returns
6373
+ */
6374
+ clearPreProcessor() {
6375
+ this._preProcessorSet.clear();
6376
+ return this;
6377
+ }
6378
+ /** 设置
6379
+ * @param data 房屋结构数据,node环境可以为路径
6380
+ * @param width 墙体宽度
6381
+ * @param scale 缩放比例
6382
+ * @param axisAlignmentCorrection 需要执行轴对称垂直纠正
6383
+ * @param option
6384
+ * @returns
6385
+ */
6386
+ async set(data, options = {}) {
6387
+ if (typeof data === "string") {
6388
+ if (typeof global !== "undefined") {
6389
+ const packageName = "fs";
6390
+ const { default: fs2 } = await import(
6391
+ /* @vite-ignore */
6392
+ packageName
6393
+ );
6394
+ const buffer = fs2.readFileSync(data);
6395
+ const json = JSON.parse(buffer.toString("utf-8"));
6396
+ return this.set(json, options);
6397
+ } else throw new Error("非node环境不允许使用路径");
6535
6398
  }
6536
- const margin = 2 * data.scale;
6537
- const colors = {
6538
- cyan: "cyan",
6539
- yellow: "yellow",
6540
- white: "white"
6541
- };
6542
- canvas.width = data.width + margin * 2;
6543
- canvas.height = data.height + margin * 2;
6544
- const ctx = canvas.getContext("2d");
6545
- if (background) {
6546
- ctx.fillStyle = background;
6547
- ctx.fillRect(0, 0, canvas.width, canvas.height);
6399
+ if (data.length === 0) return;
6400
+ this.doorLineSegment.length = 0;
6401
+ this.cad = null;
6402
+ if (data.length === 0) {
6403
+ this.lineSegments = [];
6404
+ this.originalData = data;
6405
+ } else {
6406
+ const res = this.preprocessing(data, options);
6407
+ data = res.data;
6408
+ this.lineSegments = res.lineSegments;
6409
+ this.originalData = data;
6548
6410
  }
6549
- ctx.translate(data.width * 0.5 + margin, data.height * 0.5 + margin);
6550
- ctx.scale(1, -1);
6551
- data.line.forEach(([p1x, p1y, p2x, p2y, color]) => {
6552
- ctx.strokeStyle = colors[color];
6553
- ctx.beginPath();
6554
- ctx.moveTo(p1x, p1y);
6555
- ctx.lineTo(p2x, p2y);
6556
- ctx.closePath();
6557
- ctx.stroke();
6558
- });
6559
- data.arc.forEach(([x, y, radius, startAngle, endAngle, color]) => {
6560
- ctx.strokeStyle = colors[color];
6561
- ctx.beginPath();
6562
- ctx.arc(x, y, radius, startAngle * (Math.PI / 180), endAngle * (Math.PI / 180));
6563
- ctx.stroke();
6411
+ const zList = [];
6412
+ data.forEach(({ start, end }) => zList.push(start.z ?? 0, end.z ?? 0));
6413
+ this.originalZAverage = zList.reduce((count, num) => count + num, 0) / zList.length;
6414
+ this.box = Box2.fromByLineSegment(...this.lineSegments);
6415
+ this.dispatchEvent({
6416
+ type: "dataChange",
6417
+ originalData: this.originalData,
6418
+ data: this.data
6564
6419
  });
6565
- ctx.beginPath();
6566
- data.dimensionLine.forEach((item) => {
6567
- let [p1x, p1y, _, p2y] = item;
6568
- const minY = Math.min(p1y, p2y), maxY = Math.max(p1y, p2y), x = (canvas.width * 0.5 - 0.4 * data.scale) * (p1x < 0 ? -1 : 1), h = (maxY - minY) * 0.45;
6569
- ctx.fillStyle = "#fff";
6570
- ctx.font = `${0.15 * data.scale}px Arial`;
6571
- ctx.textAlign = "center";
6572
- ctx.textBaseline = "middle";
6573
- ctx.save();
6574
- ctx.translate(x, minY + (maxY - minY) * 0.5);
6575
- ctx.scale(1, -1);
6576
- ctx.fillText((maxY - minY).toFixed(2) + "cm", 0, 0);
6577
- ctx.restore();
6578
- ctx.moveTo(x - 0.1 * data.scale, minY);
6579
- ctx.lineTo(x + 0.1 * data.scale, minY);
6580
- ctx.moveTo(x, minY);
6581
- ctx.lineTo(x, h + minY);
6582
- ctx.moveTo(x, maxY);
6583
- ctx.lineTo(x, maxY - h);
6584
- ctx.moveTo(x - 0.1 * data.scale, maxY);
6585
- ctx.lineTo(x + 0.1 * data.scale, maxY);
6420
+ this.dispatchEvent({
6421
+ type: "setDta",
6422
+ originalData: this.originalData,
6423
+ data: this.data
6586
6424
  });
6587
- ctx.closePath();
6588
- ctx.strokeStyle = "#fff";
6589
- ctx.stroke();
6590
- if ("toBlob" in canvas) {
6591
- return new Promise((resolve) => {
6592
- canvas.toBlob((b) => {
6593
- resolve(b);
6594
- }, type, 1);
6595
- });
6596
- } else {
6597
- const buffer = canvas.toBuffer(type, { quality: 1 });
6598
- return buffer;
6599
- }
6600
- }
6601
- /**
6602
- * 将点json结构转换为Dxf string
6603
- */
6604
- toDxfString(unit = "Millimeters") {
6605
- const d = new Drawing();
6606
- d.setUnits(unit);
6607
- d.addLayer("cyan", Drawing.ACI.CYAN, "DOTTED");
6608
- d.addLayer("yellow", Drawing.ACI.YELLOW, "DOTTED");
6609
- d.addLayer("white", Drawing.ACI.WHITE, "DOTTED");
6610
- const data = this.toDrawDataJson();
6611
- data.line.forEach((item) => {
6612
- let [p1x, p1y, p2x, p2y, color] = item;
6613
- d.setActiveLayer(color);
6614
- d.drawLine(p1x, p1y, p2x, p2y);
6425
+ let lines = this.getLineSegments(true);
6426
+ const doors = lines.filter((line) => line.userData.isDoor);
6427
+ lines = lines.filter((line) => !line.userData.isDoor);
6428
+ lines = buildDoubleWallGroup(lines, doors);
6429
+ lines.push(...doors);
6430
+ this.cad = new CAD().usePlugin(new DxfDataPlugin(lines)).usePlugin(new DxfDrawPlugin());
6431
+ this.dispatchEvent({
6432
+ type: "cadChange",
6433
+ cad: this.cad
6615
6434
  });
6616
- data.arc.forEach((item) => {
6617
- const [x, y, r, startAngle, endAngle, color] = item;
6618
- d.setActiveLayer(color);
6619
- d.drawArc(x, y, r, startAngle, endAngle);
6435
+ this.dispatchEvent({
6436
+ type: "lineOffset",
6437
+ cad: this.cad
6620
6438
  });
6621
- return d.toDxfString();
6622
6439
  }
6623
6440
  /**
6624
- * 将点云结构转换为DXF格式
6625
- * @returns
6441
+ * 线段数据转为原始json数据
6626
6442
  */
6627
- toDxfBlob(unit = "Millimeters") {
6628
- const blob = new Blob([this.toDxfString(unit)]);
6629
- return blob;
6443
+ lineDataToOriginalData(lines, quadtree) {
6444
+ return lineDataToOriginalData(lines, this.originalZAverage, quadtree);
6630
6445
  }
6631
6446
  /**
6632
6447
  * 下载原始json
@@ -6643,277 +6458,44 @@ class Dxf extends Component {
6643
6458
  a.click();
6644
6459
  } else if (typeof global !== "undefined") {
6645
6460
  const fs2 = await include("fs", false);
6646
- fs2.writeFileSync(filename, content);
6647
- }
6648
- }
6649
- /**
6650
- * 下载
6651
- * @param filename
6652
- */
6653
- async download(filename, unit = "Millimeters") {
6654
- if (typeof window !== "undefined") {
6655
- const blob = this.toDxfBlob(unit);
6656
- const a = document.createElement("a");
6657
- a.href = URL.createObjectURL(blob);
6658
- a.download = filename + ".dxf";
6659
- a.click();
6660
- } else if (typeof global !== "undefined") {
6661
- const fs2 = await include("fs", false);
6662
- fs2.writeFileSync(filename, this.toDxfString(unit));
6663
- }
6664
- }
6665
- /**
6666
- * 下载
6667
- * @param filename
6668
- */
6669
- async downloadImage(filename, unit = "Centimeters", type = "image/jpeg") {
6670
- const blob = await this.toDxfImageBlob(unit, type);
6671
- if (!blob) return false;
6672
- if (typeof window !== "undefined") {
6673
- const a = document.createElement("a");
6674
- a.href = URL.createObjectURL(blob);
6675
- a.download = filename;
6676
- a.click();
6677
- } else if (typeof global !== "undefined") {
6678
- const fs2 = await include("fs", false);
6679
- fs2.writeFileSync(filename, blob);
6680
- } else {
6681
- console.error("图片下载失败");
6682
- }
6683
- return true;
6684
- }
6685
- /**
6686
- * 计算原始数据的边界框
6687
- * @description 计算所有线段的起点和终点的最小最大值,形成一个边界框
6688
- * @returns
6689
- */
6690
- computedOriginalSize(data, originalBox = new Box2(0, 0, 0, 0)) {
6691
- const xArr = data.flatMap((item) => [item.start.x, item.end.x]);
6692
- const yArr = data.flatMap((item) => [item.start.y, item.end.y]);
6693
- const minX = Math.min(...xArr);
6694
- const minY = Math.min(...yArr);
6695
- const maxX = Math.max(...xArr);
6696
- const maxY = Math.max(...yArr);
6697
- originalBox.set(minX, minY, maxX, maxY);
6698
- return originalBox;
6699
- }
6700
- /**
6701
- * 创建数据
6702
- * @param pointsGroups
6703
- * @returns
6704
- */
6705
- static createData(pointsGroups, sealed = true) {
6706
- let count = 0;
6707
- const data = pointsGroups.flatMap((points) => {
6708
- const lines = points.map((point, index2) => {
6709
- const nextIndex = index2 === points.length - 1 ? 0 : index2 + 1;
6710
- const nextPoint = points[nextIndex];
6711
- return {
6712
- start: { x: point.x, y: point.y },
6713
- end: { x: nextPoint.x, y: nextPoint.y },
6714
- insetionArr: [
6715
- {
6716
- index: nextIndex + count
6717
- }
6718
- ]
6719
- };
6720
- });
6721
- count += points.length;
6722
- if (!sealed) {
6723
- lines.pop();
6724
- lines[lines.length - 1].insetionArr.length = 0;
6725
- count--;
6726
- }
6727
- return lines;
6728
- });
6729
- return data;
6730
- }
6731
- }
6732
- class Variable extends Component {
6733
- static name = "Variable";
6734
- originalLineVisible = true;
6735
- dxfVisible = true;
6736
- whiteModelVisible = true;
6737
- isLook = false;
6738
- currentWheel = 0;
6739
- pointerMove = { x: 0, y: 0 };
6740
- currentKeyUp = "";
6741
- currentKeyDown = "";
6742
- currentMouseUp = "";
6743
- currentMouseDown = "";
6744
- focus = false;
6745
- set(key, value) {
6746
- if (key in this) {
6747
- const oldValue = this[key];
6748
- this[key] = value;
6749
- this.dispatchEvent({
6750
- type: key,
6751
- value,
6752
- oldValue
6753
- });
6754
- }
6755
- }
6756
- get(key) {
6757
- if (key in this) return this[key];
6758
- }
6759
- }
6760
- class LineAnalysis extends Component {
6761
- static name = "LineAnalysis";
6762
- Dxf = null;
6763
- Variable = null;
6764
- lineSegmentList = [];
6765
- container = new THREE.Group();
6766
- // 误差角度
6767
- errorAngle = 4;
6768
- width = 0.4;
6769
- /**
6770
- *
6771
- * @param parent
6772
- */
6773
- onAddFromParent(parent) {
6774
- this.Dxf = parent.findComponentByType(Dxf);
6775
- this.Variable = this.parent?.findComponentByType(Variable);
6776
- this.Dxf.addEventListener("setDta", () => {
6777
- this.lineAnalysis();
6778
- this.dispatchEvent({ type: "analysisCompleted" });
6779
- });
6780
- }
6781
- /**
6782
- *
6783
- * @param p1
6784
- * @param p2
6785
- * @param width
6786
- * @returns
6461
+ fs2.writeFileSync(filename, content);
6462
+ }
6463
+ }
6464
+ /** 获取绘制数据
6787
6465
  */
6788
- expandLineSegment(p1, p2, width = 0.1) {
6789
- const normal = p2.normal(p1);
6790
- const pDirect = p2.direction(p1).mutiplyScalar(width * 0.5);
6791
- const nDirect = p1.direction(p2).mutiplyScalar(width * 0.5);
6792
- const offsetX = normal.x * width * 0.5;
6793
- const offsetY = normal.y * width * 0.5;
6794
- return {
6795
- points: [
6796
- // 第一条线
6797
- new Point(p1.x + offsetX, p1.y + offsetY).add(nDirect),
6798
- new Point(p2.x + offsetX, p2.y + offsetY).add(pDirect),
6799
- // 第二条线
6800
- new Point(p1.x - offsetX, p1.y - offsetY).add(nDirect),
6801
- new Point(p2.x - offsetX, p2.y - offsetY).add(pDirect)
6802
- ],
6803
- indices: [0, 1, 1, 3, 3, 2, 2, 0],
6804
- rectIndices: [0, 1, 3, 2, 0]
6805
- };
6466
+ toDrawData(unit = "Millimeters") {
6467
+ return this.cad?.toDrawData(unit);
6806
6468
  }
6807
- appendLineSegmentList = [];
6808
- /**
6809
- * 追加数据
6810
- * @param p1
6811
- * @param p2
6469
+ /** 获取 dxf 图片 二进制对象
6470
+ * @param type
6812
6471
  */
6813
- addData(p1, p2) {
6814
- const dxf = this.Dxf;
6815
- dxf.data.push([p1.clone(), p2.clone(), [], false, dxf.data.length]);
6816
- this.appendLineSegmentList.push(new LineSegment(p1.clone(), p2.clone()));
6472
+ async toDxfImageBlob(unit = "Centimeters", type = "image/jpeg", background = "#000") {
6473
+ return this.cad?.toDxfImageBlob(unit, type, background);
6817
6474
  }
6818
- /** 结果分析创建矩形
6819
- * @param result
6475
+ /** 下载 dxf 图片
6476
+ * @param filename
6820
6477
  */
6821
- createRectangle(result) {
6822
- const dxf = this.Dxf;
6823
- const project0 = result.project, project1 = result.project2;
6824
- if (project0.includedAngle(project1) > 135) {
6825
- project1.points = [project1.points[1], project1.points[0]];
6826
- }
6827
- this.addData(project0.points[0], project1.points[0]);
6828
- this.addData(project0.points[1], project1.points[1]);
6829
- const leftHeight = project0.points[0].distance(project1.points[0]), rightHeight = project0.points[1].distance(project1.points[1]), count = Math.ceil(Math.max(leftHeight, rightHeight) / dxf.width), leftFragment = leftHeight / count, rightFragment = rightHeight / count, leftDirection = project1.points[0].direction(project0.points[0]), rightDirection = project1.points[1].direction(project0.points[1]), leftP = project0.points[0].clone(), rightP = project0.points[1].clone(), direction = rightP.direction(leftP);
6830
- direction.multiplyScalar(dxf.width * 0.5);
6831
- const _leftP = leftP.clone().add(direction), _rightP = rightP.clone().add(direction.multiplyScalar(-1)), d1 = leftP.direction(rightP), d2 = _leftP.direction(_rightP);
6832
- if (d1.x > 0 && d2.x < 0 || d1.x < 0 && d2.x > 0 || d1.y > 0 && d2.y < 0 || d1.y < 0 && d2.y > 0) return;
6833
- leftP.set(_leftP.x, _leftP.y);
6834
- rightP.set(_rightP.x, _rightP.y);
6835
- for (let i = 1; i < count; i++) {
6836
- const left = leftDirection.clone().multiplyScalar(leftFragment * i), right = rightDirection.clone().multiplyScalar(rightFragment * i), p1 = leftP.clone().add(left), p2 = rightP.clone().add(right);
6837
- this.addData(p1, p2);
6838
- }
6478
+ async downloadDxfImage(filename, unit = "Centimeters", type = "image/jpeg") {
6479
+ return this.cad?.downloadDxfImage(filename, unit, type);
6839
6480
  }
6840
- quadtree;
6841
6481
  /**
6842
- * 构建线段四叉树,快速查找,
6482
+ * 将点json结构转换为Dxf 字符串
6843
6483
  */
6844
- buildQuadtree() {
6845
- const dxf = this.Dxf;
6846
- const lineSegmentList = [];
6847
- this.quadtree = new Quadtree(dxf.originalBox, 2);
6848
- dxf.lineSegments.forEach((lineSegment) => {
6849
- if (lineSegment.userData?.isDoor) return;
6850
- this.quadtree?.insert({
6851
- line: lineSegment,
6852
- userData: lineSegmentList.length
6853
- });
6854
- lineSegmentList.push(lineSegment);
6855
- });
6856
- this.lineSegmentList = lineSegmentList;
6484
+ toDxfString(unit = "Millimeters") {
6485
+ return this.cad?.toDxfString(unit);
6857
6486
  }
6858
- resultList = [];
6859
- mergeWallLines = [];
6860
- /** 线段分析
6861
- * @description 判断两条线段距离是否较短且趋近平行,然后查找两条线段的重合部分的投影线,以此判断两根线是否需要合并
6862
- * @param data
6487
+ /**
6488
+ * 将点云结构转换为 DXF 二进制对象
6489
+ * @returns
6863
6490
  */
6864
- lineAnalysis() {
6865
- this.buildQuadtree();
6866
- const quadtree = this.quadtree;
6867
- const lineSegmentList = this.lineSegmentList;
6868
- const visited = /* @__PURE__ */ new Set(), resultList = [];
6869
- lineSegmentList.forEach((_0, i) => {
6870
- const sourceLineSegment = lineSegmentList[i], rectangle = Rectangle.fromByLineSegment(sourceLineSegment, this.width * 2, false, -0.01), ids = quadtree.queryRect(rectangle).map((i2) => i2.userData).filter((index2) => index2 !== i);
6871
- ids.forEach((id) => {
6872
- try {
6873
- if (visited.has(`${i}-${id}`) || visited.has(`${id}-${i}`)) return;
6874
- const res = this.projectionAnalysis(id, i, sourceLineSegment, lineSegmentList);
6875
- if (res) resultList.push(res);
6876
- visited.add(`${i}-${id}`);
6877
- } catch (error) {
6878
- }
6879
- });
6880
- });
6881
- this.appendLineSegmentList.length = 0;
6882
- this.resultList = resultList;
6491
+ toDxfBlob(unit = "Millimeters") {
6492
+ return this.cad?.toDxfBlob(unit);
6883
6493
  }
6884
- /** 线段投影分析
6885
- * @param index
6886
- * @param sourceLineSegment
6887
- * @param lineSegmentList
6888
- * @returns
6494
+ /** 下载Dxf
6495
+ * @param filename
6889
6496
  */
6890
- projectionAnalysis(index2, sourceIndex, sourceLineSegment, lineSegmentList) {
6891
- const temLineSegment = lineSegmentList[index2], direct = sourceLineSegment.direction(), temDirect = temLineSegment.direction(), angle = direct.angleBetween(temDirect) / (Math.PI / 180);
6892
- if (angle < this.errorAngle || angle > 180 - this.errorAngle) {
6893
- let data;
6894
- const p1 = temLineSegment.projectLineSegment(sourceLineSegment), p2 = sourceLineSegment.projectLineSegment(temLineSegment);
6895
- if (p1.getLength() > p2.getLength()) {
6896
- data = {
6897
- target: temLineSegment,
6898
- targetIndex: index2,
6899
- source: sourceLineSegment,
6900
- sourceIndex,
6901
- project: p1,
6902
- project2: p2
6903
- };
6904
- } else {
6905
- data = {
6906
- target: sourceLineSegment,
6907
- targetIndex: sourceIndex,
6908
- source: temLineSegment,
6909
- sourceIndex: index2,
6910
- project: p2,
6911
- project2: p1
6912
- };
6913
- }
6914
- if (!data || data.project.getLength() < 0.2 || data.project2.getLength() < 0.2) return;
6915
- return data;
6916
- }
6497
+ async downloadDxf(filename, unit = "Millimeters") {
6498
+ return this.cad?.downloadDxf(filename, unit);
6917
6499
  }
6918
6500
  }
6919
6501
  class DoorsAnalysis {
@@ -6933,15 +6515,13 @@ class DoorsAnalysis {
6933
6515
  doorSearchNearAngle = 110;
6934
6516
  doorSearchDistance = 2;
6935
6517
  doors = [];
6936
- lineAnalysis;
6937
6518
  continueFind = true;
6938
- constructor(lineAnalysis, skipFindDoor) {
6939
- this.lineAnalysis = lineAnalysis;
6940
- this.dxf = lineAnalysis.Dxf;
6519
+ constructor(dxf, skipFindDoor) {
6520
+ this.dxf = dxf;
6521
+ this.lineSegments = dxf.lineSegments.filter((lineSegment) => !lineSegment.userData?.isDoor);
6941
6522
  this.findPointVirtualGrid = new PointVirtualGrid();
6942
- this.quadtree = lineAnalysis.quadtree;
6943
- this.resultList = lineAnalysis.resultList;
6944
- this.lineSegments = lineAnalysis.lineSegmentList;
6523
+ this.quadtree = createQuadtree(this.lineSegments);
6524
+ this.resultList = DoubleWallHelper.findDoubleLine(this.lineSegments).resultList;
6945
6525
  this.dxf.doorLineSegment.length = 0;
6946
6526
  this.lineSegments.forEach((line) => {
6947
6527
  line.points.forEach((p) => {
@@ -7050,20 +6630,13 @@ class DoorsAnalysis {
7050
6630
  */
7051
6631
  getDoorPoint() {
7052
6632
  const doorPoints = [], calculatedDoorPoint = [], dxf = this.dxf, pointVirtualGrid = this.pointVirtualGrid;
7053
- dxf.doors.forEach((item) => {
7054
- const doorLine = dxf.lineSegments[item[4]];
7055
- const doorData = dxf.originalData[item[4]];
6633
+ dxf.lineSegments.forEach((doorLine, index2) => {
6634
+ if (!doorLine.userData?.isDoor) return;
6635
+ const doorData = doorLine.userData;
7056
6636
  if (doorData.doorDirectConnection) {
7057
- const { start, end, ...opt } = doorData;
7058
6637
  this.continueFind = false;
7059
- const line = new LineSegment(Point.from(start), Point.from(end));
7060
- line.userData = {
7061
- ...cloneUserData(opt),
7062
- doorDirectConnection: true,
7063
- isDoor: true
7064
- };
7065
- this.dxf.doorLineSegment.push(line);
7066
- line.points.forEach((point) => {
6638
+ this.dxf.doorLineSegment.push(doorLine);
6639
+ doorLine.points.forEach((point) => {
7067
6640
  const res = pointVirtualGrid.queryPoint(point);
7068
6641
  if (res.length) {
7069
6642
  calculatedDoorPoint.push({
@@ -7090,7 +6663,7 @@ class DoorsAnalysis {
7090
6663
  uuid: uuid()
7091
6664
  });
7092
6665
  } else {
7093
- console.warn(`门的线段顺序${item[4]} 没有drawDoorData属性`);
6666
+ console.warn(`门的线段顺序${index2} 没有drawDoorData属性`);
7094
6667
  }
7095
6668
  });
7096
6669
  return { doorPoints, calculatedDoorPoint };
@@ -7354,7 +6927,6 @@ class DoorsAnalysisComponent extends Component {
7354
6927
  doorSearchDistance = 2;
7355
6928
  DoorsAnalysis;
7356
6929
  skipFindDoor = false;
7357
- needsSaveDoor = false;
7358
6930
  doors = [];
7359
6931
  /**
7360
6932
  *
@@ -7362,164 +6934,13 @@ class DoorsAnalysisComponent extends Component {
7362
6934
  */
7363
6935
  onAddFromParent(parent) {
7364
6936
  const dxf = parent.findComponentByType(Dxf);
7365
- const lineAnalysis = this.parent?.findComponentByType(LineAnalysis);
7366
- dxf.addEventListener("createGroup", () => {
7367
- this.DoorsAnalysis = new DoorsAnalysis(lineAnalysis, this.skipFindDoor);
7368
- if (this.needsSaveDoor) {
7369
- this.doors = dxf.doors.map((item) => dxf.lineSegments[item[4]]);
7370
- }
6937
+ dxf.addEventListener("dataChange", () => {
6938
+ this.DoorsAnalysis = new DoorsAnalysis(dxf, this.skipFindDoor);
7371
6939
  this.skipFindDoor = false;
7372
6940
  this.dispatchEvent({ type: "analysisCompleted" });
7373
6941
  });
7374
6942
  }
7375
6943
  }
7376
- function createGroup(lineSegments) {
7377
- const doors = [];
7378
- lineSegments = lineSegments.filter((line) => {
7379
- if (line.userData.isDoor) {
7380
- doors.push(line);
7381
- return false;
7382
- }
7383
- return true;
7384
- });
7385
- lineSegments = buildDoubleWallGroup(lineSegments);
7386
- lineSegments = DoubleWallHelper.complementSide(lineSegments);
7387
- lineSegments = buildDoubleWallGroup(lineSegments, doors, true, true);
7388
- return [...lineSegments, ...doors];
7389
- }
7390
- function getGroups(lines, updateGroup = true) {
7391
- if (updateGroup) lines = createGroup(lines);
7392
- const map = lines.reduce((map2, line) => {
7393
- const groups = LineGroupType.get(line);
7394
- groups.forEach((group2) => {
7395
- let id = group2.id ?? "wall";
7396
- if (group2.type === "wall") id = "wall";
7397
- map2.append(id, line);
7398
- });
7399
- return map2;
7400
- }, new ArrayMap());
7401
- return map;
7402
- }
7403
- function handleGroup(map) {
7404
- const group2 = map.group((lines) => {
7405
- if (LineGroupType.everyType(lines, "bayWindow")) return "wall";
7406
- if (LineGroupType.everyType(lines, "doubleWall")) return "doubleWall";
7407
- return "wall";
7408
- }), walls = group2.get("wall") ?? [], doubleWalls = group2.get("doubleWall") ?? [];
7409
- let newlines = [], doubleWallsSet = new Set(doubleWalls.flat());
7410
- walls.forEach((lines) => {
7411
- lines.forEach((line) => doubleWallsSet.has(line) || newlines.push(line));
7412
- });
7413
- doubleWalls.forEach((lines) => {
7414
- lines = [...new Set(lines)];
7415
- const groups = clippingDoubleWall(lines);
7416
- groups.forEach((lines2) => {
7417
- if (lines2.length < 4) return newlines.push(...lines2);
7418
- lines2 = lines2.sort((a, b) => a.length() - b.length());
7419
- const line1 = lines2[0], line2 = lines2[1];
7420
- const newLine = new LineSegment(line1.center.clone(), line2.center.clone());
7421
- mergeLineUserData(newLine, lines2);
7422
- newLine.userData.wallWidth = line1.length();
7423
- newlines.push(newLine);
7424
- mergeWindow(newLine);
7425
- });
7426
- });
7427
- newlines = newlines.map((line) => line.clone());
7428
- return newlines.filter((line) => !line.userData.isBayWindow);
7429
- }
7430
- function toJson(lineSegments, name = "测试", communityName = "") {
7431
- const scale = 1, idMap = /* @__PURE__ */ new Map();
7432
- let index2 = 0;
7433
- return {
7434
- version: "2",
7435
- name,
7436
- communityName,
7437
- city: "",
7438
- province: "",
7439
- height: 2.8 * scale,
7440
- walls: lineSegments.map((line) => {
7441
- if (line.userData.isDoor && !line.userData.doorDirectConnection) return;
7442
- idMap.set(line, index2);
7443
- return {
7444
- ID: index2++,
7445
- start: { x: line.start.x * scale, y: line.start.y * scale },
7446
- end: { x: line.end.x * scale, y: line.end.y * scale },
7447
- thickness: (line.userData.wallWidth ? line.userData.wallWidth : 0.12) * scale,
7448
- type: "LINE",
7449
- isDoor: line.userData.isDoor,
7450
- loadBearingWall: false,
7451
- height: 2.8 * scale
7452
- };
7453
- }).filter((i) => !!i),
7454
- pillars: [],
7455
- beams: [],
7456
- holes: lineSegments.flatMap((line) => {
7457
- if (line.userData.isDoor && line.userData.doorDirectConnection) {
7458
- return {
7459
- id: index2++,
7460
- type: "DOOR",
7461
- openSide: "RIGHT",
7462
- start: {
7463
- x: line.start.x * scale,
7464
- y: line.start.y * scale
7465
- },
7466
- end: {
7467
- x: line.end.x * scale,
7468
- y: line.end.y * scale
7469
- },
7470
- height: 2.1 * scale,
7471
- qroundClearance: 0 * scale,
7472
- sillHeight: (line.userData?.sillHeight ?? 0) * scale
7473
- };
7474
- } else if (line.userData.isWindow && line.userData.drawWindow) {
7475
- return line.userData.drawWindow.map((item) => {
7476
- const center = Point.from(item.p);
7477
- const start = center.clone().add(
7478
- line.direction().multiplyScalar(item.width * 0.5)
7479
- );
7480
- const end = center.clone().add(
7481
- line.direction().multiplyScalar(-item.width * 0.5)
7482
- );
7483
- return {
7484
- id: index2++,
7485
- type: "WINDOW",
7486
- start: {
7487
- x: start.x * scale,
7488
- y: start.y * scale
7489
- },
7490
- end: {
7491
- x: end.x * scale,
7492
- y: end.y * scale
7493
- },
7494
- height: 1.6 * scale,
7495
- groundClearance: (line.userData?.groundClearance ?? 0.9) * scale,
7496
- sillHeight: (line.userData?.groundClearance ?? 0.9) * scale
7497
- };
7498
- });
7499
- }
7500
- }).filter((i) => !!i),
7501
- rooms: []
7502
- };
7503
- }
7504
- function lineDataToThreeVJiaJson(lineSegments, angle = 0, updateGroup = true) {
7505
- const group2 = getGroups(lineSegments, updateGroup);
7506
- let newLines = handleGroup(group2);
7507
- newLines = LineSegmentUndirectedGraph.rotate(newLines, angle, (line, center, angle2) => {
7508
- if (line.userData.drawWindow) {
7509
- line.userData.drawWindow.forEach((windowItem) => {
7510
- const point = Point.from(windowItem.p);
7511
- point.rotate(center, angle2 * (Math.PI / 180));
7512
- windowItem.p = point.toJson(windowItem.p.z);
7513
- });
7514
- }
7515
- });
7516
- return {
7517
- lines: newLines,
7518
- toJson(name = "测试", communityName = "") {
7519
- return toJson(newLines, name, communityName);
7520
- }
7521
- };
7522
- }
7523
6944
  class ThreeVJia extends Component {
7524
6945
  static name = "ThreeVJia";
7525
6946
  lineSegments = [];
@@ -7569,10 +6990,37 @@ class ThreeVJia extends Component {
7569
6990
  }
7570
6991
  }
7571
6992
  }
6993
+ class Variable extends Component {
6994
+ static name = "Variable";
6995
+ originalLineVisible = true;
6996
+ dxfVisible = true;
6997
+ whiteModelVisible = true;
6998
+ isLook = false;
6999
+ currentWheel = 0;
7000
+ pointerMove = { x: 0, y: 0 };
7001
+ currentKeyUp = "";
7002
+ currentKeyDown = "";
7003
+ currentMouseUp = "";
7004
+ currentMouseDown = "";
7005
+ focus = false;
7006
+ set(key, value) {
7007
+ if (key in this) {
7008
+ const oldValue = this[key];
7009
+ this[key] = value;
7010
+ this.dispatchEvent({
7011
+ type: key,
7012
+ value,
7013
+ oldValue
7014
+ });
7015
+ }
7016
+ }
7017
+ get(key) {
7018
+ if (key in this) return this[key];
7019
+ }
7020
+ }
7572
7021
  class DxfSystem extends ComponentManager {
7573
7022
  Dxf;
7574
7023
  Variable;
7575
- LineAnalysis;
7576
7024
  DoorsAnalysis;
7577
7025
  CorrectionDxf;
7578
7026
  wallWidth;
@@ -7588,11 +7036,9 @@ class DxfSystem extends ComponentManager {
7588
7036
  this.Dxf = new Dxf(this.wallWidth);
7589
7037
  this.CorrectionDxf = new CorrectionDxf(this.wallWidth);
7590
7038
  this.Variable = new Variable();
7591
- this.LineAnalysis = new LineAnalysis();
7592
7039
  this.DoorsAnalysis = new DoorsAnalysisComponent();
7593
7040
  this.addComponent(this.Variable);
7594
7041
  this.addComponent(this.Dxf);
7595
- this.addComponent(this.LineAnalysis);
7596
7042
  this.addComponent(this.DoorsAnalysis);
7597
7043
  this.addComponent(this.CorrectionDxf);
7598
7044
  this.addComponent(new ThreeVJia());
@@ -7612,187 +7058,12 @@ class DxfSystem extends ComponentManager {
7612
7058
  }
7613
7059
  }
7614
7060
  const components = {
7615
- LineAnalysis,
7616
7061
  ThreeVJia,
7617
7062
  Variable,
7618
7063
  Dxf,
7619
7064
  DoorsAnalysisComponent,
7620
7065
  CorrectionDxf
7621
7066
  };
7622
- function findRingEdges(lines) {
7623
- const lineUG = new LineSegmentUndirectedGraph(lines), unionFindSet = new UnionFindSet(lineUG.size), visited = /* @__PURE__ */ new Set(), ringEdges = [];
7624
- function dfs(n1) {
7625
- visited.add(n1);
7626
- const neighbors = lineUG.getNeighbors(n1);
7627
- neighbors?.forEach((n2) => {
7628
- if (visited.has(n2)) return;
7629
- if (unionFindSet.find(n1) === unionFindSet.find(n2)) {
7630
- const line = lineUG.getLine(n1, n2);
7631
- ringEdges.push(line);
7632
- } else {
7633
- unionFindSet.union(n1, n2);
7634
- }
7635
- });
7636
- }
7637
- lineUG.forEach((n1) => {
7638
- if (visited.has(n1)) return;
7639
- dfs(n1);
7640
- });
7641
- return ringEdges;
7642
- }
7643
- function findMinRing(lines, ringEdges) {
7644
- const grid = createPointVirtualGrid(lines), temLine = new LineSegment(), queryLine = new LineSegment(), rings = [], visited = /* @__PURE__ */ new Set();
7645
- function getDirect(line, preDirect) {
7646
- const center = line.center, direct = line.normal();
7647
- temLine.start.copy(center);
7648
- temLine.end.copy(center).add(direct.clone().multiplyScalar(1));
7649
- const point = preDirect.getIntersection(temLine);
7650
- temLine.end.copy(point);
7651
- return temLine.clone();
7652
- }
7653
- function find(edge, line, point, preLine, preDirect, path, isPreDirect = true) {
7654
- if (path.length > lines.length) return;
7655
- if (visited.has(line)) return;
7656
- visited.add(line);
7657
- let direct;
7658
- const otherPoint = line.getAnotherPoint(point);
7659
- if (isPreDirect) {
7660
- if (line.vertical(preLine)) direct = getDirect(line, preDirect);
7661
- else {
7662
- const center = line.center, x = center.distance(preDirect.start), d0 = center.direction(preDirect.start);
7663
- direct = preDirect.directionMove(d0, x);
7664
- }
7665
- } else direct = preDirect;
7666
- let result = grid.queryPoint(otherPoint, true);
7667
- if (result.length === 1) {
7668
- const item = result[0], nextLine = item.userData, otherPoint1 = nextLine.getAnotherPoint(item.point), d0 = otherPoint1.direction(item.point);
7669
- if (edge === nextLine) return path;
7670
- if (nextLine.vertical(line)) {
7671
- const d1 = otherPoint.direction(point), center = nextLine.center, isSameDirection = d0.angleBetween(direct.direction()) < 1e-9;
7672
- direct.start.copy(center);
7673
- direct.end.copy(center).add(d1.multiplyScalar(0.02));
7674
- if (isSameDirection) direct.end.rotate(direct.start, Math.PI);
7675
- path.push(nextLine);
7676
- if (find(edge, nextLine, item.point, line, direct, path, false)) return path;
7677
- }
7678
- } else {
7679
- queryLine.start.copy(direct.start).add(direct.direction().multiplyScalar(5e-3));
7680
- const lines2 = [line, ...result.map((item) => {
7681
- const line2 = item.userData;
7682
- line2.length();
7683
- return line2;
7684
- })];
7685
- for (let i = 0; i < result.length; i++) {
7686
- const item = result[i], nextLine = item.userData;
7687
- if (edge === nextLine) return path;
7688
- if (result.length === 4 && nextLine.parallel(line)) continue;
7689
- queryLine.end.copy(nextLine.center);
7690
- if (!lines2.some((line1) => {
7691
- if (line1 === nextLine) return false;
7692
- return line1.intersectLineSegment(queryLine);
7693
- })) {
7694
- path.push(nextLine);
7695
- return find(edge, nextLine, item.point, line, direct, path);
7696
- }
7697
- }
7698
- }
7699
- }
7700
- for (let i = 0; i < ringEdges.length; i++) {
7701
- const line = ringEdges[i], center = line.center, startList = grid.queryPoint(line.start, true), endList = grid.queryPoint(line.end, true), minList = startList.length < endList.length ? startList : endList, directList = [], list = minList.filter((item) => {
7702
- if (minList.length === 3 && item.userData?.parallel(line)) return false;
7703
- const otherPoint = item.userData?.getAnotherPoint(item.point);
7704
- directList.push(otherPoint.direction(item.point));
7705
- return true;
7706
- });
7707
- const ringList = [];
7708
- for (let i2 = 0; i2 < list.length; i2++) {
7709
- const item = list[i2];
7710
- let direct = directList[i2];
7711
- if (item.userData?.parallel(line)) {
7712
- const otherIndex = i2 ? 0 : 1, other = directList[otherIndex];
7713
- direct = other.clone().multiplyScalar(-1);
7714
- }
7715
- visited.clear();
7716
- const directLine = new LineSegment(center.clone(), center.clone().add(direct.clone().multiplyScalar(0.2)));
7717
- const path = find(line, item.userData, item.point, line, directLine, [line, item.userData]);
7718
- if (path) {
7719
- ringList.push([...path]);
7720
- }
7721
- }
7722
- if (ringList.length === 2) {
7723
- const set2 = new Set(ringList[0]);
7724
- const count = ringList[1].filter((line2) => set2.has(line2)).length;
7725
- if (count > 1) ringList.splice(0, 2, ringList[0].length < ringList[1].length ? ringList[0] : ringList[1]);
7726
- }
7727
- rings.push(...ringList);
7728
- }
7729
- return rings;
7730
- }
7731
- function ringsDeduplication(rings) {
7732
- const map = new ArrayMap(), newRings = [];
7733
- rings.forEach((ring) => map.append(ring.length, ring));
7734
- for (const rings2 of map.values()) {
7735
- const lineIndexGenerator = new LineIndexGenerator(rings2.flat());
7736
- const ringMap = new ArrayMap();
7737
- rings2.map((ring) => {
7738
- const key = ring.map((line) => lineIndexGenerator.getIndex(line)).sort((a, b) => a - b).join(",");
7739
- ringMap.append(key, ring);
7740
- });
7741
- ringMap.forEach((list) => newRings.push(list[0]));
7742
- }
7743
- return newRings;
7744
- }
7745
- function findMaxRing(lines, ringEdges) {
7746
- const rings = findMinRing(lines, ringEdges), visited = /* @__PURE__ */ new Map(), unionFindSet = new UnionFindSet(rings.length), internalEdges = [];
7747
- rings.forEach((ring, index2) => {
7748
- for (let i = 0; i < ring.length; i++) {
7749
- const line = ring[i];
7750
- if (visited.has(line)) unionFindSet.union(index2, visited.get(line));
7751
- else visited.set(line, index2);
7752
- }
7753
- });
7754
- const countMap = new CountMap();
7755
- const groups = unionFindSet.getAllSets().valueArray.map((list) => {
7756
- let group2 = list.map((i) => rings[i]);
7757
- if (group2.length === 1) return group2[0];
7758
- countMap.clear();
7759
- group2 = ringsDeduplication(group2);
7760
- group2.forEach((lines2) => lines2.forEach((line) => countMap.set(line)));
7761
- const ring = countMap.reduce((lines2, count, line) => {
7762
- if (count === 1) lines2.push(line);
7763
- else internalEdges.push(line);
7764
- return lines2;
7765
- }, []);
7766
- return ring;
7767
- });
7768
- return {
7769
- internalEdges,
7770
- rings: groups
7771
- };
7772
- }
7773
- function findClosedPolygons(lines) {
7774
- const linesGroup = LineSegment.groupByPath(lines), rings = [], internalEdges = [];
7775
- linesGroup.forEach((lines2, _) => {
7776
- try {
7777
- let ringEdges = findRingEdges(lines2);
7778
- const result = findMaxRing(lines2, ringEdges);
7779
- rings.push(...result.rings);
7780
- internalEdges.push(...result.internalEdges);
7781
- } catch (error) {
7782
- console.warn("环查找出现异常:", error.message);
7783
- }
7784
- });
7785
- const internalEdgesSet = /* @__PURE__ */ new Set([...internalEdges]);
7786
- const groupsSet = /* @__PURE__ */ new Set([...rings.flat()]);
7787
- lines = lines.filter((line) => !internalEdgesSet.has(line));
7788
- lines.forEach((line) => !groupsSet.has(line));
7789
- lines = [...new Set(lines)];
7790
- return {
7791
- newLines: lines,
7792
- rings,
7793
- internalEdges
7794
- };
7795
- }
7796
7067
  function drawLines$1(lines, parameters, offset = 1e-3) {
7797
7068
  if (Array.isArray(lines) && lines[0] instanceof Point) {
7798
7069
  const _lines = lines;
@@ -7998,7 +7269,7 @@ class Scenario {
7998
7269
  this.doorGroup.name = "";
7999
7270
  this.texture = null;
8000
7271
  this.numdu = 1;
8001
- this.color = 15066597;
7272
+ this.color = 16777215;
8002
7273
  this.doorModely = null;
8003
7274
  this.windowModely = null;
8004
7275
  this.CabinetModel = null;
@@ -8060,10 +7331,17 @@ class Scenario {
8060
7331
  this.closedArray = lines.filter((item) => {
8061
7332
  return LineGroupType.hasType(item, "doubleWall");
8062
7333
  });
8063
- const groups = LineSegment.groupByPath(this.closedArray);
7334
+ let groups = LineSegment.groupByPath(this.closedArray);
8064
7335
  const set2 = new Set(this.closedArray);
8065
7336
  let singleLineWall = lines.filter((line) => !set2.has(line) && !line.userData.isDoor && !line.userData.isBayWindow);
8066
7337
  let menList = lines.filter((line) => line.userData.isDoor);
7338
+ groups = groups.filter((group2) => {
7339
+ if (group2.length < 4) {
7340
+ singleLineWall.push(...group2);
7341
+ return false;
7342
+ }
7343
+ return true;
7344
+ });
8067
7345
  this.TheHandlingOfTheDoor(menList);
8068
7346
  this.overallTreatmentOfSingleLineWalls(singleLineWall);
8069
7347
  for (const g in groups) {
@@ -8166,15 +7444,13 @@ class Scenario {
8166
7444
  const geometry = this.createParallelepipedFromBase(basePoints, 2.8);
8167
7445
  const material = new THREE.MeshStandardMaterial({
8168
7446
  color: this.color,
8169
- side: THREE.DoubleSide,
8170
- // flatShading: true,
8171
- roughness: this.numdu,
8172
- metalness: 0,
8173
- envMapRotation: new THREE.Euler(0, 0, 0),
8174
- polygonOffset: true,
8175
- polygonOffsetFactor: 10,
8176
- polygonOffsetUnits: 10
8177
- // wireframe: true
7447
+ side: THREE.DoubleSide
7448
+ // roughness: this.numdu,
7449
+ // metalness: 0.0,
7450
+ // envMapRotation: new THREE.Euler(0, 0, 0),
7451
+ // polygonOffset: true,
7452
+ // polygonOffsetFactor: 10,
7453
+ // polygonOffsetUnits: 10,
8178
7454
  });
8179
7455
  const cube = new THREE.Mesh(geometry, material);
8180
7456
  const edges = new THREE.EdgesGeometry(geometry);
@@ -8471,14 +7747,13 @@ class Scenario {
8471
7747
  const geometry = this.createParallelepipedFromBase(basePoints, Height ? Height : this.WindowHeight);
8472
7748
  const material = new THREE.MeshStandardMaterial({
8473
7749
  color: this.color,
8474
- side: THREE.DoubleSide,
8475
- // flatShading: true,
8476
- roughness: this.numdu,
8477
- metalness: 0,
8478
- envMapRotation: new THREE.Euler(0, 0, 0),
8479
- polygonOffset: true,
8480
- polygonOffsetFactor: 10,
8481
- polygonOffsetUnits: 10
7750
+ side: THREE.DoubleSide
7751
+ // roughness: this.numdu,
7752
+ // metalness: 0.0,
7753
+ // envMapRotation: new THREE.Euler(0, 0, 0),
7754
+ // polygonOffset: true,
7755
+ // polygonOffsetFactor: 10,
7756
+ // polygonOffsetUnits: 10,
8482
7757
  });
8483
7758
  const brush1 = new Brush(mesh.geometry);
8484
7759
  brush1.updateMatrixWorld();
@@ -8524,25 +7799,23 @@ class Scenario {
8524
7799
  const result = evaluator.evaluate(brush1, brush2, SUBTRACTION);
8525
7800
  const material = new THREE.MeshStandardMaterial({
8526
7801
  color: this.color,
8527
- side: THREE.DoubleSide,
8528
- // flatShading: true,
8529
- roughness: this.numdu,
8530
- metalness: 0,
8531
- envMapRotation: new THREE.Euler(0, 0, 0),
8532
- polygonOffset: true,
8533
- polygonOffsetFactor: 10,
8534
- polygonOffsetUnits: 10
7802
+ side: THREE.DoubleSide
7803
+ // roughness: this.numdu,
7804
+ // metalness: 0.0,
7805
+ // envMapRotation: new THREE.Euler(0, 0, 0),
7806
+ // polygonOffset: true,
7807
+ // polygonOffsetFactor: 10,
7808
+ // polygonOffsetUnits: 10,
8535
7809
  });
8536
7810
  const material1 = new THREE.MeshStandardMaterial({
8537
7811
  color: this.color,
8538
- side: THREE.DoubleSide,
8539
- // flatShading: true,
8540
- roughness: this.numdu,
8541
- metalness: 0,
8542
- envMapRotation: new THREE.Euler(0, 0, 0),
8543
- polygonOffset: true,
8544
- polygonOffsetFactor: 10,
8545
- polygonOffsetUnits: 10
7812
+ side: THREE.DoubleSide
7813
+ // roughness: this.numdu,
7814
+ // metalness: 0.0,
7815
+ // envMapRotation: new THREE.Euler(0, 0, 0),
7816
+ // polygonOffset: true,
7817
+ // polygonOffsetFactor: 10,
7818
+ // polygonOffsetUnits: 10,
8546
7819
  });
8547
7820
  result.material = material1;
8548
7821
  new THREE.Mesh(geometry2, material);
@@ -15842,9 +15115,9 @@ class SceneAutoGenerat {
15842
15115
  });
15843
15116
  const contour = Point.fromByList(item.contour ?? []), rectangle = new Polygon(Qa(contour.map((p) => [p.x, p.y])).map((p) => Point.from(p)));
15844
15117
  rectangle.pop();
15845
- const max = rectangle.getMaxLengthInfo(), min = rectangle.getMinLengthInfo(), center = rectangle.getCenter(), direction = max.start.y < max.end.y ? max.start.direction(max.end) : max.end.direction(max.start), height = Math.abs(item.box.max.z - this.z);
15118
+ const z = item.box.min.z, max = rectangle.getMaxLengthInfo(), min = rectangle.getMinLengthInfo(), center = rectangle.getCenter(), direction = max.start.y < max.end.y ? max.start.direction(max.end) : max.end.direction(max.start), height = Math.abs(item.box.max.z - z);
15846
15119
  const group2 = new THREE.Group();
15847
- group2.position.set(center.x, center.y, this.z);
15120
+ group2.position.set(center.x, center.y, z);
15848
15121
  group2.add(model);
15849
15122
  this.scene.add(group2);
15850
15123
  const box32 = new THREE.Box3();
@@ -15866,7 +15139,7 @@ class SceneAutoGenerat {
15866
15139
  const direction2 = point1.y < point2.y ? point1.direction(point2) : point2.direction(point1);
15867
15140
  group2.rotateZ(direction2.angleBetween2(direction));
15868
15141
  this.scene.add(
15869
- SceneAutoGenerat.itemParse(item, this.z).box
15142
+ SceneAutoGenerat.itemParse(item).box
15870
15143
  );
15871
15144
  }
15872
15145
  /** 构建物品模型
@@ -15874,10 +15147,10 @@ class SceneAutoGenerat {
15874
15147
  async buildItem() {
15875
15148
  await Promise.all(this.itemList.map(async (item) => await this.getModel(item)));
15876
15149
  }
15877
- static itemParse(item, z) {
15150
+ static itemParse(item) {
15878
15151
  const contour = Point.fromByList(item.contour), rectangle = new Polygon(Qa(contour.map((p) => [p.x, p.y])).map((p) => Point.from(p)));
15879
15152
  rectangle.pop();
15880
- const height = Math.abs(item.box.max.z - z), max = rectangle.getMaxLengthInfo(), direction = max.start.y < max.end.y ? max.start.direction(max.end) : max.end.direction(max.start), shape = new THREE.Shape();
15153
+ const z = item.box.min.z, height = Math.abs(item.box.max.z - z), max = rectangle.getMaxLengthInfo(), min = rectangle.getMinLengthInfo(), direction = max.start.y < max.end.y ? max.start.direction(max.end) : max.end.direction(max.start), shape = new THREE.Shape();
15881
15154
  rectangle.forEach((p, i) => {
15882
15155
  if (i === 0) shape.moveTo(p.x, p.y);
15883
15156
  else shape.lineTo(p.x, p.y);
@@ -15890,11 +15163,34 @@ class SceneAutoGenerat {
15890
15163
  lineSegments.position.z = z;
15891
15164
  box3.setFromObject(lineSegments);
15892
15165
  const center = box3.getCenter(new THREE.Vector3());
15166
+ const maxLine = new LineSegment(max.start, max.end), minLine = new LineSegment(min.start, min.end);
15167
+ const zStart = new THREE.Vector3().copy(rectangle[0].toJson(z)), zEnd = zStart.clone();
15168
+ zEnd.z += height;
15893
15169
  return {
15894
15170
  box: lineSegments,
15895
15171
  center,
15896
15172
  category: item.category,
15897
- angle: direction.angleBetween(new Point(0, 1), "angle", "360") * (Math.PI / 180) + Math.PI * 0.5
15173
+ angle: direction.angleBetween(new Point(0, 1), "angle", "360") * (Math.PI / 180) + Math.PI * 0.5,
15174
+ info: [
15175
+ {
15176
+ center: new THREE.Vector3().copy(maxLine.center.toJson(z)),
15177
+ start: new THREE.Vector3().copy(maxLine.start.toJson(z)),
15178
+ end: new THREE.Vector3().copy(maxLine.start.toJson(z)),
15179
+ length: maxLine.length()
15180
+ },
15181
+ {
15182
+ center: new THREE.Vector3().copy(minLine.center.toJson(z)),
15183
+ start: new THREE.Vector3().copy(minLine.start.toJson(z)),
15184
+ end: new THREE.Vector3().copy(minLine.start.toJson(z)),
15185
+ length: minLine.length()
15186
+ },
15187
+ {
15188
+ center: zStart.clone().add(zEnd).multiplyScalar(0.5),
15189
+ start: zStart,
15190
+ end: zEnd,
15191
+ length: height
15192
+ }
15193
+ ]
15898
15194
  };
15899
15195
  }
15900
15196
  }
@@ -15927,8 +15223,6 @@ class WhiteModel extends Component {
15927
15223
  whiteModelGroup = new THREE.Group();
15928
15224
  // dxf数据白模边缘线
15929
15225
  whiteModelLineGroup = new THREE.Group();
15930
- // 原始数据白模
15931
- originalWhiteMode = new THREE.Group();
15932
15226
  material = new THREE.MeshStandardMaterial({ color: 16777215, transparent: true, opacity: 0.8, side: THREE.DoubleSide });
15933
15227
  itemList = [];
15934
15228
  promise;
@@ -15942,8 +15236,7 @@ class WhiteModel extends Component {
15942
15236
  onAddFromParent(parent) {
15943
15237
  this.Dxf = parent.findComponentByName("Dxf");
15944
15238
  this.Variable = parent.findComponentByName("Variable");
15945
- this.originalWhiteMode.visible = false;
15946
- this.Dxf?.addEventListener("lineOffset", () => {
15239
+ this.Dxf?.addEventListener("cadChange", () => {
15947
15240
  this.promise = this.updateModel();
15948
15241
  });
15949
15242
  }
@@ -15951,12 +15244,10 @@ class WhiteModel extends Component {
15951
15244
  const dxfSystem = this.parent;
15952
15245
  this.Variable?.set("whiteModelVisible", false);
15953
15246
  const dxf = this.Dxf;
15954
- this.originalWhiteMode.clear();
15955
15247
  this.whiteModelGroup.clear();
15956
15248
  this.whiteModelLineGroup.clear();
15957
15249
  this.whiteModelGroup.add(this.whiteModelLineGroup);
15958
15250
  this.whiteModelGroup.position.z = dxf.originalZAverage;
15959
- this.originalWhiteMode.position.z = dxf.originalZAverage;
15960
15251
  const lines = dxfSystem.Dxf.getLineSegments(true);
15961
15252
  const sceneAutoGenerat = new SceneAutoGenerat(lines, this.itemList, dxfSystem.Dxf.originalZAverage, dxfSystem.CorrectionDxf.trajectory);
15962
15253
  await sceneAutoGenerat.init();
@@ -15965,7 +15256,7 @@ class WhiteModel extends Component {
15965
15256
  this.whiteModelLineGroup.add(group2.clone(true));
15966
15257
  }
15967
15258
  const walls = dxf.originalData.map(({ start, end, insetionArr }) => {
15968
- const startVec3 = new Point(start.x, start.y).mutiplyScalar(dxf.scale), endVec3 = new Point(end.x, end.y).mutiplyScalar(dxf.scale), { points, indices, rectIndices } = lineSqueezing(startVec3, endVec3, dxf.width);
15259
+ const startVec3 = new Point(start.x, start.y), endVec3 = new Point(end.x, end.y), { points, indices, rectIndices } = lineSqueezing(startVec3, endVec3, dxf.width);
15969
15260
  return {
15970
15261
  points,
15971
15262
  indices,
@@ -15985,12 +15276,9 @@ class WhiteModel extends Component {
15985
15276
  bevelSize: 0
15986
15277
  });
15987
15278
  if (geometry.attributes.position.array.filter((num) => Number.isNaN(num)).length) return;
15988
- const mesh = new THREE.Mesh(geometry);
15989
- this.originalWhiteMode?.add(mesh);
15990
15279
  });
15991
15280
  this.dispatchEvent({
15992
15281
  type: "updateModel",
15993
- originalWhiteMode: this.originalWhiteMode,
15994
15282
  whiteModelGroup: this.whiteModelGroup
15995
15283
  });
15996
15284
  return this.whiteModelLineGroup;
@@ -16099,101 +15387,6 @@ class WhiteModel extends Component {
16099
15387
  }
16100
15388
  }
16101
15389
  }
16102
- class DetailsPoint extends Component {
16103
- static name = "DetailsPoint";
16104
- Dxf = null;
16105
- WhiteModel = null;
16106
- Variable = null;
16107
- desPoints = [];
16108
- raylines = [];
16109
- data = [];
16110
- onAddFromParent(parent) {
16111
- this.Dxf = parent.findComponentByName("Dxf");
16112
- this.Variable = parent.findComponentByName("Variable");
16113
- this.Dxf?.addEventListener("setDta", () => {
16114
- this.updateModel();
16115
- });
16116
- }
16117
- /**
16118
- * 设置值
16119
- * @param data
16120
- */
16121
- async set(data) {
16122
- if (typeof data === "string") {
16123
- if (typeof global !== "undefined") {
16124
- const packageName = "fs";
16125
- const { default: fs2 } = await import(
16126
- /* @vite-ignore */
16127
- packageName
16128
- );
16129
- const buffer = fs2.readFileSync(data);
16130
- const json = JSON.parse(buffer.toString("utf-8"));
16131
- this.set(json);
16132
- return;
16133
- } else {
16134
- throw new Error("非node环境不允许使用路径");
16135
- }
16136
- }
16137
- this.data = data;
16138
- this.updateModel();
16139
- }
16140
- /**
16141
- * 设置射线辅助
16142
- */
16143
- racasterHelper(position, direction, far) {
16144
- this.raylines.push([
16145
- position.clone(),
16146
- position.clone().add(direction.clone().multiplyScalar(far))
16147
- ]);
16148
- direction.z = 0;
16149
- this.raylines.push([
16150
- position.clone(),
16151
- position.clone().add(direction.clone().multiplyScalar(far))
16152
- ]);
16153
- }
16154
- _timer = null;
16155
- /**
16156
- * 更新模型
16157
- */
16158
- updateModel() {
16159
- if (this._timer) clearTimeout(this._timer);
16160
- this._timer = setTimeout(() => {
16161
- this._timer = null;
16162
- const whiteModel = this.parent?.findComponentByName("WhiteModel");
16163
- this.raylines.length = 0;
16164
- this.desPoints.length = 0;
16165
- this.data.forEach((item) => {
16166
- const position = new THREE.Vector3(
16167
- item.position.x,
16168
- item.position.y,
16169
- item.position.z
16170
- );
16171
- const direction = new THREE.Vector3(
16172
- item.direction.x,
16173
- item.direction.y,
16174
- item.direction.z
16175
- );
16176
- const far = 100;
16177
- this.racasterHelper(position, direction, far);
16178
- direction.z = 0;
16179
- const raycaster = new THREE.Raycaster(position, direction, 0, far);
16180
- const list = raycaster.intersectObject(whiteModel.originalWhiteMode);
16181
- if (list.length) {
16182
- const { point } = list[0];
16183
- this.desPoints.push({
16184
- message: item.desc,
16185
- position,
16186
- intersection: point
16187
- });
16188
- }
16189
- });
16190
- this.dispatchEvent({
16191
- type: "handleSuccess",
16192
- desPoints: this.desPoints
16193
- });
16194
- }, 50);
16195
- }
16196
- }
16197
15390
  class DxfLineModel extends Component {
16198
15391
  static name = "DxfLineModel";
16199
15392
  dxfLineModel = new THREE.LineSegments();
@@ -16204,16 +15397,22 @@ class DxfLineModel extends Component {
16204
15397
  this.dxfModelGroup.add(this.dxfLineModel);
16205
15398
  this.dxfModelGroup.add(this.dxfDoorsLineModel);
16206
15399
  this.dxfDoorsLineModel.material = new THREE.LineBasicMaterial({ color: 16776960, vertexColors: true });
16207
- dxf?.addEventListener("lineOffset", () => this.updateMode());
15400
+ dxf?.addEventListener("cadChange", () => this.updateMode());
16208
15401
  }
16209
15402
  updateMode() {
16210
15403
  const dxf = this.parent?.findComponentByName("Dxf");
16211
15404
  this.dxfLineModel.clear();
16212
- const dxfArray = dxf.to3DArray(1 / dxf.scale, 0);
15405
+ const cad = dxf.cad;
15406
+ const dxfArray = new Float32Array(
15407
+ cad.groups.flatMap((group2) => {
15408
+ if (group2.type !== "unionGroupAll") return [];
15409
+ return group2.lines.flatMap((line) => line.points.flatMap((p) => [p.x, p.y, 0]));
15410
+ })
15411
+ );
16213
15412
  this.dxfLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(dxfArray, 3, true));
16214
15413
  const doorsArray = new Float32Array(
16215
15414
  dxf.doorLineSegment.flatMap(({ start, end }) => [start.x, start.y, 0, end.x, end.y, 0])
16216
- ).map((n) => n / dxf.scale);
15415
+ );
16217
15416
  const doorsColorArray = new Float32Array(dxf.doorLineSegment.flatMap(() => [1, 0, 0, 0, 1, 0]));
16218
15417
  this.dxfDoorsLineModel.geometry = new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(doorsArray, 3, true)).setAttribute("color", new THREE.BufferAttribute(doorsColorArray, 3));
16219
15418
  this.dxfModelGroup.position.z = dxf.originalZAverage;
@@ -16225,19 +15424,16 @@ class DxfLineModel extends Component {
16225
15424
  }
16226
15425
  const index$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
16227
15426
  __proto__: null,
16228
- DetailsPoint,
16229
15427
  DxfLineModel,
16230
15428
  WhiteModel
16231
15429
  }, Symbol.toStringTag, { value: "Module" }));
16232
15430
  function ModelDataPlugin_(dxfSystem, option = {}) {
16233
15431
  const {
16234
- detailsPoint = true,
16235
15432
  whiteModel = true,
16236
15433
  dxfLineModel = true
16237
15434
  } = option;
16238
15435
  dxfLineModel && dxfSystem.addComponent(new DxfLineModel());
16239
15436
  whiteModel && dxfSystem.addComponent(new WhiteModel());
16240
- detailsPoint && dxfSystem.addComponent(new DetailsPoint());
16241
15437
  }
16242
15438
  const ModelDataPlugin = Object.assign(ModelDataPlugin_, {
16243
15439
  create(option = {}) {
@@ -16638,12 +15834,9 @@ async function createEditor(dom, camera, orbitControls = false, viewPermission)
16638
15834
  const rp = await loadRenderPlugin();
16639
15835
  const editor = await loadEditorPlugin();
16640
15836
  const dxfSystem = new DxfSystem().usePlugin(mp.ModelDataPlugin.create({
16641
- detailsPoint: false,
16642
15837
  whiteModel: true
16643
15838
  })).usePlugin(rp.RenderPlugin.create({
16644
- originalLine: false,
16645
15839
  modelData: false,
16646
- detailsPoint: false,
16647
15840
  orbitControls,
16648
15841
  camera
16649
15842
  })).usePlugin(editor.EditorPlugin.create({ viewPermission }));
@@ -16687,45 +15880,44 @@ async function buildJson(opt) {
16687
15880
  dxfSystem.usePlugin(mp.ModelDataPlugin);
16688
15881
  const whiteModel = dxfSystem.findComponentByType(mp.components.WhiteModel);
16689
15882
  const threeVJia = dxfSystem.findComponentByName("ThreeVJia");
16690
- await dxfSystem.Dxf.set(json);
16691
- if (trajectory) {
16692
- if (typeof trajectory === "string") {
15883
+ if (itemInfo) {
15884
+ if (typeof itemInfo === "string") {
16693
15885
  if (typeof global !== "undefined") {
16694
15886
  const packageName = "fs";
16695
15887
  const { default: fs2 } = await import(
16696
15888
  /* @vite-ignore */
16697
15889
  packageName
16698
15890
  );
16699
- const buffer = fs2.readFileSync(trajectory);
16700
- trajectory = JSON.parse(buffer.toString("utf-8"));
15891
+ const buffer = fs2.readFileSync(itemInfo);
15892
+ itemInfo = JSON.parse(buffer.toString("utf-8"));
16701
15893
  } else throw new Error("非node环境不允许使用路径");
16702
15894
  }
16703
- dxfSystem.CorrectionDxf.setTrajectory(trajectory);
15895
+ whiteModel?.setItemList(itemInfo);
16704
15896
  }
16705
- if (itemInfo) {
16706
- if (typeof itemInfo === "string") {
15897
+ if (opt.axisAlignCorr !== false) dxfSystem.Dxf.addPreProcessor(PRE_PROCESSOR.AxisAlignCorr);
15898
+ if (trajectory) {
15899
+ if (typeof trajectory === "string") {
16707
15900
  if (typeof global !== "undefined") {
16708
15901
  const packageName = "fs";
16709
15902
  const { default: fs2 } = await import(
16710
15903
  /* @vite-ignore */
16711
15904
  packageName
16712
15905
  );
16713
- const buffer = fs2.readFileSync(itemInfo);
16714
- itemInfo = JSON.parse(buffer.toString("utf-8"));
15906
+ const buffer = fs2.readFileSync(trajectory);
15907
+ trajectory = JSON.parse(buffer.toString("utf-8"));
16715
15908
  } else throw new Error("非node环境不允许使用路径");
16716
15909
  }
16717
- whiteModel?.setItemList(itemInfo);
15910
+ dxfSystem.CorrectionDxf.setTrajectory(trajectory);
16718
15911
  }
16719
- await dxfSystem.Dxf.axisAlignCorr({
15912
+ await dxfSystem.Dxf.set(json, {
16720
15913
  groupMethod: "cross",
16721
15914
  fittingMethod: "max",
16722
15915
  crossAxistThreshold: 0.08,
16723
15916
  ...axisAlignCorrOption
16724
15917
  });
16725
- dxfSystem.Dxf.lineOffset();
16726
15918
  download?.json && await dxfSystem.Dxf.downloadOriginalData(download.json);
16727
- download?.dxf && await dxfSystem.Dxf.download(download.dxf);
16728
- download?.image && await dxfSystem.Dxf.downloadImage(download.image);
15919
+ download?.dxf && await dxfSystem.Dxf.downloadDxf(download.dxf);
15920
+ download?.image && await dxfSystem.Dxf.downloadDxfImage(download.image);
16729
15921
  download?.correctionJson && await dxfSystem.CorrectionDxf.downloadOriginalData(download.correctionJson);
16730
15922
  download?.correctionDxf && await dxfSystem.CorrectionDxf.downloadDxf(download.correctionDxf);
16731
15923
  download?.correctionImage && await dxfSystem.CorrectionDxf.downloadDxfImage(download.correctionImage);
@@ -16741,7 +15933,7 @@ async function buildJson(opt) {
16741
15933
  async function getFileAll(dxfSystem = gloabalDxfSystem) {
16742
15934
  const whiteModel = dxfSystem.findComponentByName("WhiteModel");
16743
15935
  const jpg = new File([await dxfSystem.CorrectionDxf.toDxfImageBlob()], "img.jpg", { type: "image/jpeg" });
16744
- const dxf = new File([dxfSystem.Dxf.toDxfBlob()], "dxf.dxf", { type: "application/dxf" });
15936
+ const dxf = new File([dxfSystem.Dxf.toDxfBlob() ?? ""], "dxf.dxf", { type: "application/dxf" });
16745
15937
  const correctionDxf = new File([dxfSystem.CorrectionDxf.toDxfBlob() ?? ""], "dxf.dxf", { type: "application/dxf" });
16746
15938
  const glb = new File([await whiteModel.toGltfBlob(true)], "model.glb", { type: "application/octet-stream" });
16747
15939
  const gltf2 = new File([await whiteModel.toGltfBlob(false)], "model.gltf", { type: "application/json" });
@@ -16764,29 +15956,28 @@ export {
16764
15956
  Box2 as B,
16765
15957
  Component as C,
16766
15958
  DxfSystem as D,
16767
- Lines as L,
15959
+ LineSegment as L,
16768
15960
  ModelDataPlugin as M,
16769
- Point as P,
15961
+ PointVirtualGrid as P,
16770
15962
  Quadtree as Q,
16771
15963
  SelectLocalFile as S,
16772
15964
  ThreeVJia as T,
16773
15965
  Variable as V,
16774
15966
  WhiteModel as W,
16775
- DetailsPoint as a,
16776
- PointVirtualGrid as b,
15967
+ Point as a,
15968
+ DoorsAnalysisComponent as b,
16777
15969
  cloneUserData as c,
16778
- DoorsAnalysisComponent as d,
16779
- LineSegment as e,
16780
- CommandManager as f,
16781
- createEditor as g,
16782
- getModels as h,
16783
- buildJson as i,
16784
- getFileAll as j,
16785
- getGlobalDxfSystem as k,
16786
- Dxf as l,
16787
- index$1 as m,
16788
- index as n,
16789
- components as o,
15970
+ Lines as d,
15971
+ CommandManager as e,
15972
+ createEditor as f,
15973
+ getModels as g,
15974
+ buildJson as h,
15975
+ getFileAll as i,
15976
+ getGlobalDxfSystem as j,
15977
+ Dxf as k,
15978
+ index$1 as l,
15979
+ index as m,
15980
+ components as n,
16790
15981
  recomputedWindow as r,
16791
15982
  uuid as u
16792
15983
  };