|
@@ -165,9 +165,9 @@
|
|
|
</el-button>
|
|
|
<el-button
|
|
|
type="primary"
|
|
|
- @click="onAddPointSave"
|
|
|
+ @click="onAddPoint"
|
|
|
>
|
|
|
- 保存
|
|
|
+ 确定
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</el-form>
|
|
@@ -179,30 +179,40 @@
|
|
|
import * as d3 from "d3";
|
|
|
import { getWholeData } from "./api.js";
|
|
|
import { ElLoading } from 'element-plus'
|
|
|
-import {computePointDistanceAndRowSlope} from '@/utils.js'
|
|
|
+import {getDistance2D, computePointDistanceAndRowSlope, getNeighbourLocations} from '@/utils.js'
|
|
|
|
|
|
// 视口尺寸
|
|
|
let svgWidth = document.documentElement.clientWidth - 200
|
|
|
let svgHeight = document.documentElement.clientHeight - 280
|
|
|
let svgRatio = svgWidth / svgHeight
|
|
|
|
|
|
-let pxPerUnitLength = 0 // 原始数据1单位长度对应的像素数
|
|
|
+// 全体点位数据
|
|
|
let rawWholeData = []
|
|
|
let wholeDataForRender = []
|
|
|
+
|
|
|
+// 用户输入的起点终点
|
|
|
let startPoint = null
|
|
|
let endPoint = null
|
|
|
+
|
|
|
+// 由原始数据算出的几何信息
|
|
|
+let pxPerUnitLength = 0 // 原始数据1单位长度对应的像素数
|
|
|
let pointDistance = 0 // 最近相邻点间距离(单位:原始数据中长度单位)
|
|
|
let rowSlope = 0 // 点位构成的排的斜率 [0deg, 90deg)
|
|
|
-
|
|
|
-// 全部数据点的分布中心
|
|
|
let xCenter = 0
|
|
|
let yCenter = 0
|
|
|
|
|
|
+// svg、d3相关
|
|
|
let svgNode = null
|
|
|
let gNode = null
|
|
|
let zoomObj = null
|
|
|
let brushObj = null
|
|
|
|
|
|
+// d3选择框位置
|
|
|
+let brushLeftPx = 0
|
|
|
+let brushTopPx = 0
|
|
|
+let brushRightPx = 0
|
|
|
+let brushBottomPx = 0
|
|
|
+
|
|
|
function resetGlobalVars() {
|
|
|
pxPerUnitLength = 0 // 原始单位长度对应的像素数
|
|
|
rawWholeData = []
|
|
@@ -219,120 +229,18 @@ function zoomed({transform}) {
|
|
|
gNode.attr("transform", transform);
|
|
|
}
|
|
|
|
|
|
-function brushed(e, componentInst) {
|
|
|
+function brushed(e) {
|
|
|
if (e.selection) {
|
|
|
console.log('bursh area in px: ', e.selection[0][0], e.selection[0][1], e.selection[1][0], e.selection[1][1]);
|
|
|
- const brushLeftPx = e.selection[0][0]
|
|
|
- const brushTopPx = e.selection[0][1]
|
|
|
- const brushRightPx = e.selection[1][0]
|
|
|
- const brushBottomPx = e.selection[1][1]
|
|
|
-
|
|
|
- let translateX = 0
|
|
|
- let translateY = 0
|
|
|
- let scale = 1
|
|
|
- const gNodetransformStr = gNode.attr('transform')
|
|
|
- if (gNodetransformStr) {
|
|
|
- const translateStr = gNodetransformStr.split(' ')[0]
|
|
|
- const translateRegex = /translate\(([^,]+),([^,]+)\)/;
|
|
|
- const translateMatch = translateStr.match(translateRegex);
|
|
|
-
|
|
|
- const scaleStr = gNodetransformStr.split(' ')[1]
|
|
|
- const scaleRegex = /scale\(([^)]+)\)/;
|
|
|
- const scaleMatch = scaleStr.match(scaleRegex);
|
|
|
-
|
|
|
- translateX = Number(translateMatch[1]);
|
|
|
- translateY = Number(translateMatch[2]);
|
|
|
- scale = Number(scaleMatch[1]);
|
|
|
- }
|
|
|
- console.log('transform info: ', translateX, translateY, scale);
|
|
|
-
|
|
|
- const brushLeftPxBeformTransform = (brushLeftPx - translateX) / scale
|
|
|
- const brushTopPxBeformTransform = (brushTopPx - translateY) / scale
|
|
|
- const brushRightPxBeformTransform = (brushRightPx - translateX) / scale
|
|
|
- const brushBottomPxBeformTransform = (brushBottomPx - translateY) / scale
|
|
|
-
|
|
|
- const brushLeft = (brushLeftPxBeformTransform - svgWidth / 2) / pxPerUnitLength + xCenter
|
|
|
- const brushTop = (brushTopPxBeformTransform - svgHeight / 2) / pxPerUnitLength + yCenter
|
|
|
- const brushRight = (brushRightPxBeformTransform - svgWidth / 2) / pxPerUnitLength + xCenter
|
|
|
- const brushBottom = (brushBottomPxBeformTransform - svgHeight / 2) / pxPerUnitLength + yCenter
|
|
|
- console.log('brush area in raw coordinate: ', brushLeft, brushTop, brushRight, brushBottom);
|
|
|
-
|
|
|
- const affectionAreaLeft = brushLeft - pointDistance * 1.25
|
|
|
- const affectionAreaTop = brushTop - pointDistance * 1.25
|
|
|
- const affectionAreaRight = brushRight + pointDistance * 1.25
|
|
|
- const affectionAreaBottom = brushBottom + pointDistance * 1.25
|
|
|
- console.log('affection area in raw coordinate: ', affectionAreaLeft, affectionAreaTop, affectionAreaRight, affectionAreaBottom);
|
|
|
-
|
|
|
- // 筛选出框选区域可能影响到的所有外围点
|
|
|
- // 筛选规则:除了x、y坐标,还要看z坐标是否在当前连通规则给定的高度区间。
|
|
|
- // 筛选后,如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
- const affectedPointList = []
|
|
|
- for (const point of rawWholeData) {
|
|
|
- // 如果z不合格,pass
|
|
|
- if (Math.abs(point.z - componentInst.formData.addPointHeight) > componentInst.formData.connectionMaxHeightGap) {
|
|
|
- continue
|
|
|
- }
|
|
|
- // 如果在affection范围外,pass
|
|
|
- if (point.x < affectionAreaLeft || point.x > affectionAreaRight || point.y < affectionAreaTop || point.y > affectionAreaBottom) {
|
|
|
- continue
|
|
|
- }
|
|
|
- // 如果在框选区域内,pass
|
|
|
- if (point.x >= brushLeft && point.x <= brushRight && point.y >= brushTop && point.y <= brushBottom) {
|
|
|
- continue
|
|
|
- }
|
|
|
- affectedPointList.push(point)
|
|
|
- }
|
|
|
- // todo: 如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
- console.log('affectedPointList: ', affectedPointList);
|
|
|
-
|
|
|
- // 筛选出框选区域内所有已存在的点
|
|
|
- // 筛选规则:除了x、y坐标,还要看z坐标是否在当前连通规则给定的高度区间。
|
|
|
- // 筛选后,如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
- const pointInBrushList = []
|
|
|
- for (const point of rawWholeData) {
|
|
|
- // 如果z不合格,pass
|
|
|
- if (Math.abs(point.z - componentInst.formData.addPointHeight) > componentInst.formData.connectionMaxHeightGap) {
|
|
|
- continue
|
|
|
- }
|
|
|
- // 如果在框选区域内,留下
|
|
|
- if (point.x >= brushLeft && point.x <= brushRight && point.y >= brushTop && point.y <= brushBottom) {
|
|
|
- pointInBrushList.push(point)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- gNode.selectAll('rect')
|
|
|
- .attr('fill', (d) => {
|
|
|
- if (affectedPointList.find((affectedPoint) => {
|
|
|
- // console.log(affectedPoint.id, d[4]);
|
|
|
- return affectedPoint.id === d[4]
|
|
|
- })) {
|
|
|
- return 'blue'
|
|
|
- } else if (pointInBrushList.find((pointInBrush) => {
|
|
|
- return pointInBrush.id === d[4]
|
|
|
- })) {
|
|
|
- return 'green'
|
|
|
- } else {
|
|
|
- return `black`
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- if (!affectedPointList.length) {
|
|
|
- window.alert('请在已有点位附近新增点位。')
|
|
|
- return
|
|
|
- }
|
|
|
- if (!pointInBrushList.length) {
|
|
|
- window.alert('请保证框选区域内至少已有一个点位。')
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- let activePointIdx = 0
|
|
|
- while(activePointIdx <= pointInBrushList.length - 1) {
|
|
|
- console.log('shabi');
|
|
|
- activePointIdx++
|
|
|
- }
|
|
|
-
|
|
|
+ brushLeftPx = e.selection[0][0]
|
|
|
+ brushTopPx = e.selection[0][1]
|
|
|
+ brushRightPx = e.selection[1][0]
|
|
|
+ brushBottomPx = e.selection[1][1]
|
|
|
} else {
|
|
|
- console.log('cancel brush');
|
|
|
+ brushLeftPx = 0
|
|
|
+ brushTopPx = 0
|
|
|
+ brushRightPx = 0
|
|
|
+ brushBottomPx = 0
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -340,8 +248,8 @@ export default {
|
|
|
name: 'App',
|
|
|
data() {
|
|
|
return {
|
|
|
- // sceneNameOrUrl: 'SS-t-XkquhxxurM',
|
|
|
- sceneNameOrUrl: 'SS-t-NZUICC2fRLi',
|
|
|
+ sceneNameOrUrl: 'SS-t-XkquhxxurM',
|
|
|
+ // sceneNameOrUrl: 'SS-t-NZUICC2fRLi',
|
|
|
infoText: '',
|
|
|
loadingHandler: null,
|
|
|
formData: {
|
|
@@ -541,7 +449,7 @@ export default {
|
|
|
svgNode.call(zoomObj);
|
|
|
|
|
|
brushObj = d3.brush().on("end", (e) => {
|
|
|
- brushed(e, this)
|
|
|
+ brushed(e)
|
|
|
})
|
|
|
}).finally(() => {
|
|
|
this.loadingHandler.close()
|
|
@@ -758,8 +666,225 @@ export default {
|
|
|
onAddPointRedo() {
|
|
|
|
|
|
},
|
|
|
- onAddPointSave() {
|
|
|
+ onAddPoint() {
|
|
|
+ // 解析svg的transform信息
|
|
|
+ let translateX = 0
|
|
|
+ let translateY = 0
|
|
|
+ let scale = 1
|
|
|
+ const gNodetransformStr = gNode.attr('transform')
|
|
|
+ if (gNodetransformStr) {
|
|
|
+ const translateStr = gNodetransformStr.split(' ')[0]
|
|
|
+ const translateRegex = /translate\(([^,]+),([^,]+)\)/;
|
|
|
+ const translateMatch = translateStr.match(translateRegex);
|
|
|
+
|
|
|
+ const scaleStr = gNodetransformStr.split(' ')[1]
|
|
|
+ const scaleRegex = /scale\(([^)]+)\)/;
|
|
|
+ const scaleMatch = scaleStr.match(scaleRegex);
|
|
|
+
|
|
|
+ translateX = Number(translateMatch[1]);
|
|
|
+ translateY = Number(translateMatch[2]);
|
|
|
+ scale = Number(scaleMatch[1]);
|
|
|
+ }
|
|
|
+ console.log('transform info: ', translateX, translateY, scale);
|
|
|
+
|
|
|
+ // 选择框位置复原到svg transfrom前
|
|
|
+ const brushLeftPxBeformTransform = (brushLeftPx - translateX) / scale
|
|
|
+ const brushTopPxBeformTransform = (brushTopPx - translateY) / scale
|
|
|
+ const brushRightPxBeformTransform = (brushRightPx - translateX) / scale
|
|
|
+ const brushBottomPxBeformTransform = (brushBottomPx - translateY) / scale
|
|
|
+
|
|
|
+ // 选择框位置复原到原始坐标系
|
|
|
+ const brushLeft = (brushLeftPxBeformTransform - svgWidth / 2) / pxPerUnitLength + xCenter
|
|
|
+ const brushTop = (brushTopPxBeformTransform - svgHeight / 2) / pxPerUnitLength + yCenter
|
|
|
+ const brushRight = (brushRightPxBeformTransform - svgWidth / 2) / pxPerUnitLength + xCenter
|
|
|
+ const brushBottom = (brushBottomPxBeformTransform - svgHeight / 2) / pxPerUnitLength + yCenter
|
|
|
+ console.log('brush area in raw coordinate: ', brushLeft, brushTop, brushRight, brushBottom);
|
|
|
+
|
|
|
+ // 计算出选择框的外围影响区域
|
|
|
+ const affectionAreaLeft = brushLeft - pointDistance * Math.SQRT2 * 1.25
|
|
|
+ const affectionAreaTop = brushTop - pointDistance * Math.SQRT2 * 1.25
|
|
|
+ const affectionAreaRight = brushRight + pointDistance * Math.SQRT2 * 1.25
|
|
|
+ const affectionAreaBottom = brushBottom + pointDistance * Math.SQRT2 * 1.25
|
|
|
+ console.log('affection area in raw coordinate: ', affectionAreaLeft, affectionAreaTop, affectionAreaRight, affectionAreaBottom);
|
|
|
+
|
|
|
+ // 筛选出框选区域可能影响到的所有外围点
|
|
|
+ // 筛选规则:除了x、y坐标,还要看z坐标是否在当前连通规则给定的高度区间。
|
|
|
+ // 筛选后,如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
+ const affectedPointList = []
|
|
|
+ for (const point of rawWholeData) {
|
|
|
+ // 如果z不合格,pass
|
|
|
+ if (Math.abs(point.z - this.formData.addPointHeight) > this.formData.connectionMaxHeightGap) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 如果在affection范围外,pass
|
|
|
+ if (point.x < affectionAreaLeft || point.x > affectionAreaRight || point.y < affectionAreaTop || point.y > affectionAreaBottom) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 如果在框选区域内,pass
|
|
|
+ if (point.x >= brushLeft && point.x <= brushRight && point.y >= brushTop && point.y <= brushBottom) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ affectedPointList.push(point)
|
|
|
+ }
|
|
|
+ // todo: 如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
+ console.log('affectedPointList: ', affectedPointList);
|
|
|
+
|
|
|
+ // 筛选出框选区域内所有已存在的点
|
|
|
+ // 筛选规则:除了x、y坐标,还要看z坐标是否在当前连通规则给定的高度区间。
|
|
|
+ // 筛选后,如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
+ const pointInBrushList = []
|
|
|
+ for (const point of rawWholeData) {
|
|
|
+ // 如果z不合格,pass
|
|
|
+ if (Math.abs(point.z - this.formData.addPointHeight) > this.formData.connectionMaxHeightGap) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 如果在框选区域内,留下
|
|
|
+ if (point.x >= brushLeft && point.x <= brushRight && point.y >= brushTop && point.y <= brushBottom) {
|
|
|
+ pointInBrushList.push(point)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ gNode.selectAll('rect')
|
|
|
+ .attr('fill', (d) => {
|
|
|
+ if (affectedPointList.find((affectedPoint) => {
|
|
|
+ // console.log(affectedPoint.id, d[4]);
|
|
|
+ return affectedPoint.id === d[4]
|
|
|
+ })) {
|
|
|
+ return 'blue'
|
|
|
+ } else if (pointInBrushList.find((pointInBrush) => {
|
|
|
+ return pointInBrush.id === d[4]
|
|
|
+ })) {
|
|
|
+ return 'green'
|
|
|
+ } else {
|
|
|
+ return `black`
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ if (!affectedPointList.length) {
|
|
|
+ window.alert('请在已有点位附近新增点位。')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!pointInBrushList.length) {
|
|
|
+ window.alert('请保证框选区域内至少已有一个点位。')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let activePointIdx = 0
|
|
|
+ let newPointId = rawWholeData[rawWholeData.length - 1].id + 1
|
|
|
+ // 位于框选区域内的所有点位构成稀疏图模型,用邻接表表示,依次处理其顶点表中各顶点。
|
|
|
+ while(activePointIdx <= pointInBrushList.length - 1) {
|
|
|
+ // 拿到当前顶点
|
|
|
+ const activePoint = pointInBrushList[activePointIdx]
|
|
|
+ console.log('顶点表:', pointInBrushList);
|
|
|
+ console.log('当前处理顶点Idx:', activePointIdx);
|
|
|
+ console.log('当前处理顶点:', activePoint);
|
|
|
+
|
|
|
+ // 当前顶点有可能是本次补点前就存在的,所以清空其邻居信息
|
|
|
+ activePoint.ids = []
|
|
|
+ const tempIds1 = [] // 临时记录上下左右邻居
|
|
|
+ const tempIds2 = [] // 临时记录斜角邻居
|
|
|
+
|
|
|
+ // 拿到当前顶点的8个相邻点位
|
|
|
+ const neighbourPosList = getNeighbourLocations(pointInBrushList[activePointIdx], pointDistance, rowSlope)
|
|
|
+
|
|
|
+ // 处理8个相邻点位
|
|
|
+ for (const key in neighbourPosList) {
|
|
|
+ if (Object.hasOwnProperty.call(neighbourPosList, key)) {
|
|
|
+ // 看看这个相邻点位属于上下左右邻居还是斜角邻居。
|
|
|
+ let neighbourType = 0
|
|
|
+ if (['posRight', 'posBottom', 'posLeft', 'posTop'].incldes(key)) {
|
|
|
+ neighbourType = 1
|
|
|
+ } else {
|
|
|
+ neighbourType = 2
|
|
|
+ }
|
|
|
+
|
|
|
+ const neiPos = neighbourPosList[key];
|
|
|
+
|
|
|
+ // 如果点位在框选区域外
|
|
|
+ if (neiPos.x < brushLeft || neiPos.x > brushRight || neiPos.y < brushTop || neiPos.y > brushBottom) {
|
|
|
+ // 在外围点位表中找匹配的点
|
|
|
+ const matchedPoint = affectedPointList.find((affectedPoint) => {
|
|
|
+ return getDistance2D(affectedPoint, neiPos) < pointDistance * 0.1
|
|
|
+ })
|
|
|
+ // 如果找到了匹配点
|
|
|
+ if (matchedPoint) {
|
|
|
+ // 记录自己与其关系。
|
|
|
+ if (neighbourType === 1) {
|
|
|
+ tempIds1.push(matchedPoint.id)
|
|
|
+ } else {
|
|
|
+ tempIds2.push(matchedPoint.id)
|
|
|
+ }
|
|
|
+ // 酌情修改这个外围点位与自己的关系。
|
|
|
+
|
|
|
+ // 相邻点位数组ids中,前4项表示上下左右邻居,后四项表示斜角邻居。
|
|
|
+ let idsStartIdx = -1
|
|
|
+ let idsEndIdx = -1
|
|
|
+ if (neighbourType === 1) {
|
|
|
+ idsStartIdx = 0
|
|
|
+ idsEndIdx = 4
|
|
|
+ } else {
|
|
|
+ idsStartIdx = 4
|
|
|
+ idsEndIdx = 8
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!matchedPoint.ids.slice(idsStartIdx, idsEndIdx).find((id) => {
|
|
|
+ return id === activePoint.id
|
|
|
+ })) {
|
|
|
+ const emptyIdx = matchedPoint.ids.slice(idsStartIdx, idsEndIdx).findIndex((id) => {
|
|
|
+ return id === '-1'
|
|
|
+ })
|
|
|
+ console.assert(emptyIdx !== -1, 'emptyIdx居然不存在?!')
|
|
|
+ matchedPoint.ids[idsStartIdx + emptyIdx] = activePoint.id
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果没找到匹配点
|
|
|
+ else {
|
|
|
+ // 记录自己与其关系。
|
|
|
+ if (neighbourType === 1) {
|
|
|
+ tempIds1.push('-1')
|
|
|
+ } else {
|
|
|
+ tempIds2.push('-1')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果点位在框选区域内
|
|
|
+ else {
|
|
|
+ // 在顶点表中找匹配的点
|
|
|
+ const matchedPoint = pointInBrushList.find((pointInBrush) => {
|
|
|
+ return getDistance2D(pointInBrush, neiPos) < pointDistance * 0.1
|
|
|
+ })
|
|
|
+ // 如果找到了匹配点
|
|
|
+ if (matchedPoint) {
|
|
|
+ // 记录自己与其关系。
|
|
|
+ if (neighbourType === 1) {
|
|
|
+ tempIds1.push(matchedPoint.id)
|
|
|
+ } else {
|
|
|
+ tempIds2.push(matchedPoint.id)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果没找到匹配点
|
|
|
+ else {
|
|
|
+ // 创建新点位,加入顶点表
|
|
|
+ pointInBrushList.push({
|
|
|
+ id: newPointId,
|
|
|
+ x: neiPos.x,
|
|
|
+ y: neiPos.y,
|
|
|
+ z: this.formData.addPointHeight,
|
|
|
+ })
|
|
|
+ newPointId++
|
|
|
+
|
|
|
+ // 记录自己与其关系
|
|
|
+ if (neighbourType === 1) {
|
|
|
+ tempIds1.push(String(newPointId))
|
|
|
+ } else {
|
|
|
+ tempIds2.push(String(newPointId))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } // end of 处理8个相邻点位
|
|
|
+ activePointIdx++
|
|
|
+ } // end of 依次处理顶点表中各顶点
|
|
|
},
|
|
|
},
|
|
|
}
|