功能概览:
- 开启此功能后,鼠标会自动吸附到模型表面,在其吸附位置绘制一个与模型表面平行的小型圆面和一个与模型表面垂直的箭头。这个部分可以方便确定后续要出现的剖切面的准确位置。
- 在模型任意一点点击后,就在该位置创建剖切平面,创建的剖切平面的位置就是上面那个小型圆面的位置。
- 创建完剖切平面后,就出现了一个三维球,用户可以对球体上的旋转,平移,归位等把手进行各种组合操作,以此来实时联动三维球和剖切平面,方便对模型的各个部分进行操作和查看。
- 还提供与此相关的配套api,结束剖切与隐藏或显示剖切工具接口。
代码介绍:
在这个ts文件里暴露出了startAxialDeepCut
、 stopAxialDeepCut
、 showAxialDeepCutTool
这几个接口,可以用于开启轴向剖切、结束轴向剖切、和是否显示剖切工具。在这个文件里还引用了特地封装的三维球控制器useCoordinateBall
这一个hooks。
三维球控制器
在三维球控制器这个文件里暴露createCoordinateBall
、removeCoordinateBall
、resetCoordinateBall
、setCoordinateBallShow
、setCoordinateBallLines
这个几个接口,可以用于创建三维球控制器、移除三维球控制器、重制三维球控制器、通过接口而不是鼠标的去设置三维球控制器的各个参数和显隐。
创建三维球控制器的函数签名是
ts
const createCoordinateBall: (CoordinateBall?: CoordinateBall) => {
id: string;
emitter: EventEmitter;
}
这个函数的核心代码片段介绍:
初始参数
ts
const { longitude, latitude, height } = coordinate;
const centerPosition = Cartesian3.fromDegrees(longitude, latitude, height);
const initialM3 = new Matrix3();
initialQuaternion && Matrix3.fromQuaternion(initialQuaternion, initialM3);
ts
const { line, color, name, ring, ball, square, normalize } = p;
normalizes[name] = normalize.clone();
const worldMatrix = Transforms.eastNorthUpToFixedFrame(centerPosition);
const baseCartesian3 = Cartesian3.normalize(
Cartesian3.subtract(
Matrix4.multiplyByPoint(worldMatrix, normalize, new Cartesian3()),
centerPosition,
new Cartesian3()
),
new Cartesian3()
);
通过初始参数确定三维球在三维空间里的坐标和三维球的局部坐标
箭头绘制以及控制
ts
if (line.show) {
lines['line' + name] = {
color,
material: new PolylineArrowMaterialProperty(color.withAlpha(alpha)),
diffValue: 0,
baseCartesian3,
normalize,
};
const lineEntity = viewer.entities.add({
name: id + '/line' + name,
polyline: {
positions: new CallbackProperty(() => {
const startPosition = Cartesian3.add(
self.current.coordinateBall[id].centerPosition,
diffCartesian3,
new Cartesian3()
);
const endPosition = Cartesian3.add(
startPosition,
Cartesian3.multiplyByScalar(
self.current.coordinateBall[id].lines['line' + name].baseCartesian3,
length,
new Cartesian3()
),
new Cartesian3()
);
return [startPosition, endPosition];
}, false),
arcType: ArcType.NONE,
material: new PolylineArrowMaterialProperty(color.withAlpha(alpha)),
width: 20,
},
});
lines['line' + name].entity = lineEntity;
}
默认是要绘制三个轴向的箭头,如果这个参数为否则不绘制。这里会计算出startPosition
和endPosition
,来确定每个箭头的开始和结束位置,因为三维球会有移动的可能所以这部分的计算放在了new CallbackProperty
里,可以提高动画运行效率。
箭头可以用来设置三个轴向的移动,代码为这部分:
ts
self.current.isDownLine = true;
const line = coordinateBall.lines[name];
const { color, entity, material, baseCartesian3 } = line;
const positions = (entity!.polyline!.positions as PositionPropertyArray).getValue(new JulianDate())!;
const c0 = viewer.scene.cartesianToCanvasCoordinates(positions[0].clone());
const c1 = viewer.scene.cartesianToCanvasCoordinates(positions[1].clone());
Cartesian2.normalize(Cartesian2.subtract(c1, c0, new Cartesian2()), c2NormalVec);
self.current.pickEntity = viewer.entities.add({
polyline: {
positions: positions,
arcType: ArcType.NONE,
material: new PolylineArrowMaterialProperty(copiesColor),
width: 20,
},
});
self.current.pickGraphics = entity!.polyline!;
self.current.pickMateria = material;
self.current.pickGraphics.material = new PolylineArrowMaterialProperty(color);
const realCenterPosition = Cartesian3.add(centerPosition, diffCartesian3, new Cartesian3());
const startPosition = Cartesian3.add(
realCenterPosition,
Cartesian3.multiplyByScalar(baseCartesian3, 1500, new Cartesian3()),
new Cartesian3()
);
const endPosition = Cartesian3.add(
realCenterPosition,
Cartesian3.multiplyByScalar(baseCartesian3, -1500, new Cartesian3()),
new Cartesian3()
);
self.current.auxiliaryLine = viewer.entities.add({
polyline: {
positions: [startPosition, endPosition],
material: new PolylineDashMaterialProperty({
color: color,
dashLength: 3,
}),
width: 3,
},
});
这里会在点击的某个箭头哪里创建一条延长的虚线,以用来形象化的展示在空间里的移动的方位self.current.auxiliaryLine
。
在代码里:
ts
const diffValue = distance * dot;
setCoordinateBallLines(id, name, diffValue);
监听移动的距离直接通过上面提到的setCoordinateBallLines
去设置移动的类型以及移动的距离。
圆环绘制以及控制
ts
if (ring.show) {
const minLength = ringLength * window.Math.sqrt(3);
const maxLength = length + 0.5533;
const ringEntity = viewer.entities.add({
name: id + '/ring' + name,
position: new CallbackProperty(() => {
return Cartesian3.add(self.current.coordinateBall[id].centerPosition, diffCartesian3, new Cartesian3());
}, false) as unknown as Cartesian3,
ellipsoid: {
radii: new Cartesian3(maxLength, maxLength, maxLength),
innerRadii: new Cartesian3(minLength, minLength, minLength),
minimumCone: Math.toRadians(89.5),
maximumCone: Math.toRadians(90.5),
material: new PolylineArrowMaterialProperty(color.withAlpha(alpha)),
},
});
const m3 = Transforms.rotationMatrixFromPositionVelocity(baseCartesian3, baseCartesian3);
const rot90 = Matrix3.fromRotationY(Math.toRadians(90));
Matrix3.multiply(m3, rot90, m3);
const quaternion = Quaternion.fromRotationMatrix(m3);
ringEntity.orientation = new ConstantProperty(quaternion);
rings['ring' + name] = {
entity: ringEntity,
};
}
默认是要绘制三个轴向的圆环,如果这个参数为否则不绘制。这里会计算出圆环的中心,因为三维球会有移动的可能所以这部分的计算放在了new CallbackProperty
里,可以提高动画运行效率。还计算出了三个圆环在空间里的三维姿态quaternion
点击圆环或者小球会触发的代码逻辑是:
ts
if ((name.includes('ball') || name.includes('ring')) && !self.current.auxiliaryBall) {
const names = name.replace('ring', 'ball');
const ball = coordinateBall.balls[names];
const { color, entity, material, index, baseAngle, diffAngle } = ball;
const line = coordinateBall.lines['line' + nom[index == 0 ? 2 : index - 1]];
const positions = (line.entity!.polyline!.positions as PositionPropertyArray).getValue(new JulianDate())!;
const lienDiffPosition = Cartesian3.subtract(positions[1], positions[0], new Cartesian3());
const ballP = entity!.position!.getValue(new JulianDate())!.clone();
const c0 = viewer.scene.cartesianToCanvasCoordinates(ballP);
const c1 = viewer.scene.cartesianToCanvasCoordinates(Cartesian3.add(ballP, lienDiffPosition, new Cartesian3()));
Cartesian2.normalize(Cartesian2.subtract(c1, c0, new Cartesian2()), c2NormalVec);
const startPosition = Cartesian3.add(centerPosition, diffCartesian3, new Cartesian3());
const angle = baseAngle;
const axis = self.current.coordinateBall[id].lines['line' + nom[index]].baseCartesian3;
const rota = self.current.coordinateBall[id].lines['line' + nom[index == 2 ? 0 : index + 1]].baseCartesian3;
const quaternion = Quaternion.fromAxisAngle(axis, Math.toRadians(angle - diffAngle));
const m3 = Matrix3.fromQuaternion(quaternion);
const ballPosition = Cartesian3.add(
startPosition,
Cartesian3.multiplyByScalar(
Matrix3.multiplyByVector(m3, rota, new Cartesian3()),
ringLength,
new Cartesian3()
),
new Cartesian3()
);
self.current.pickEntity = viewer.entities.add({
position: ballPosition,
ellipsoid: {
radii: new Cartesian3(ballRadiiLength, ballRadiiLength, ballRadiiLength),
material: copiesColor,
},
});
self.current.pickGraphics = entity!.ellipsoid!;
self.current.pickMateria = material;
self.current.pickGraphics.material = new ColorMaterialProperty(color);
self.current.auxiliaryBall = viewer.entities.add({
name: names,
polyline: {
positions: new CallbackProperty(() => {
const p = entity?.position?.getValue(new JulianDate());
return [startPosition, p];
}, false),
material: new PolylineDashMaterialProperty({
color: color,
dashLength: 3,
}),
width: 3,
},
});
}
这里会先去获取当前圆环所在小球的坐标,或者就是所点击小球的坐标positions
,然后通过viewer.scene.cartesianToCanvasCoordinates
api计算小球三维坐标在屏幕二维空间的投影,得到一个基准点,方便后续鼠标操作的时候计算小球的偏移量,然后还会绘制一个self.current.pickEntity
小球(会高亮显示),以及绘制一条小球到三维球中心的虚线self.current.auxiliaryBall
小球,新绘制的小球和虚线都会跟随鼠标的移动而移动,以用来形象化的展示在空间里的旋转的方位。
方块绘制以及控制
ts
if (square.show) {
const position = new Cartesian3();
if (initialQuaternion) {
Matrix3.multiplyByVector(initialM3, p.square.position, position);
Cartesian3.multiplyByScalar(position, length / 2.5, position);
}
const squareBasePosition = Cartesian3.multiplyByScalar(p.square.position, length / 2.5, new Cartesian3());
squares['square' + name] = {
position: initialQuaternion ? position : squareBasePosition.clone(),
basePosition: squareBasePosition.clone(),
planeAxis: p.square.planeAxis,
color,
material: new ColorMaterialProperty(color.withAlpha(alpha)),
heading: p.square.heading,
pitch: p.square.pitch,
roll: p.square.roll,
};
const dimensionsLength = length / 2.5;
const dimensions = new Cartesian3(dimensionsLength, dimensionsLength, dimensionsLength);
dimensions[name.toLocaleLowerCase() as 'x' | 'y' | 'z'] = 0;
const squareEntity = viewer.entities.add({
name: id + '/square' + name,
position: new CallbackProperty(() => {
const worldMatrix = Transforms.eastNorthUpToFixedFrame(
Cartesian3.add(self.current.coordinateBall[id].centerPosition, diffCartesian3, new Cartesian3())
);
return Matrix4.multiplyByPoint(worldMatrix, squares['square' + name].position, new Cartesian3());
}, false) as unknown as Cartesian3,
box: {
dimensions: dimensions,
material: color.withAlpha(alpha),
},
orientation: new ConstantProperty(
initialQuaternion
? Transforms.headingPitchRollQuaternion(
self.current.coordinateBall[id].centerPosition,
HeadingPitchRoll.fromQuaternion(initialQuaternion)
)
: Transforms.headingPitchRollQuaternion(
self.current.coordinateBall[id].centerPosition,
new HeadingPitchRoll()
)
),
});
squares['square' + name].entity = squareEntity;
}
其他逻辑和上述的一样,再特别说明一下三维空间里的计算会大量用到坐标全局和局部的转换Transforms.eastNorthUpToFixedFrame
和旋转姿态四元数的计算Transforms.headingPitchRollQuaternion
。
点击方块会触发的代码逻辑是:
ts
if (name.includes('square') && !self.current.auxiliarySquare) {
emitter!.emit('coordinateBall/' + id, {
id,
type: 'clickSquare',
name,
});
if (squareMove) {
self.current.auxiliarySquare = { lines: [] };
const square = coordinateBall.squares[name];
const { color, planeAxis, entity, material, heading, pitch, roll, position } = square;
const worldMatrix = Transforms.eastNorthUpToFixedFrame(
Cartesian3.add(centerPosition, diffCartesian3, new Cartesian3())
);
self.current.pickEntity = viewer.entities.add({
position: Matrix4.multiplyByPoint(worldMatrix, position, new Cartesian3()),
box: {
dimensions: new Cartesian3(length / 3.5, 0.1, length / 3.5),
material: copiesColor,
},
orientation: new ConstantProperty(
Transforms.headingPitchRollQuaternion(
centerPosition,
new HeadingPitchRoll(Math.toRadians(heading), Math.toRadians(pitch), Math.toRadians(roll))
)
),
});
self.current.pickMateria = material;
self.current.pickGraphics = entity!.box!;
self.current.pickGraphics.material = new ColorMaterialProperty(color);
planeAxis.forEach((p) => {
const color = coordinateBall.lines['line' + p].color;
const realCenterPosition = Cartesian3.add(centerPosition, diffCartesian3, new Cartesian3());
const [_, key] = name.split('line');
const worldMatrix = Transforms.eastNorthUpToFixedFrame(realCenterPosition);
const startPosition = Matrix4.multiplyByPoint(
worldMatrix,
Cartesian3.multiplyByScalar(normalizes[key], 1500, new Cartesian3()),
new Cartesian3()
);
const endPosition = Matrix4.multiplyByPoint(
worldMatrix,
Cartesian3.multiplyByScalar(normalizes[key], -1500, new Cartesian3()),
new Cartesian3()
);
const line = viewer.entities.add({
polyline: {
positions: [startPosition, endPosition],
material: new PolylineDashMaterialProperty({
color: color,
dashLength: 3,
}),
width: 3,
},
});
self.current.auxiliarySquare?.lines.push(line);
});
}
}
这里主要做的逻辑是确定点击的是三块方块中的那一块const square = coordinateBall.squares[name]
,然后计算出对应的全局坐标worldMatrix
,然后把点击的小方块self.current.pickEntity
和三维球的三个箭头planeAxis.forEach
的姿态都变换到与点击方块对应的轴向平面上。
重制三维球控制器
ts
const resetCoordinateBall = (id: string) => {
self.current.coordinateBall[id].diffCartesian3 = new Cartesian3(0, 0, 0);
self.current.coordinateBall[id].fixedDiffCartesian3 = new Cartesian3(0, 0, 0);
const keys = ['X', 'Y', 'Z'];
keys.forEach((key) => {
self.current.coordinateBall[id].balls['ball' + key].baseAngle = 0;
self.current.coordinateBall[id].balls['ball' + key].diffAngle = 0;
const normalize = (self.current.coordinateBall[id].normalizes[key] = normNormalizes[key].clone());
const { centerPosition } = self.current.coordinateBall[id];
const worldMatrix = Transforms.eastNorthUpToFixedFrame(centerPosition);
const baseCartesian3 = Cartesian3.normalize(
Cartesian3.subtract(
Matrix4.multiplyByPoint(worldMatrix, normalize, new Cartesian3()),
centerPosition,
new Cartesian3()
),
new Cartesian3()
);
self.current.coordinateBall[id].lines['line' + key].baseCartesian3 = baseCartesian3;
const rotationMatrix = Transforms.rotationMatrixFromPositionVelocity(baseCartesian3, baseCartesian3);
const rot90 = Matrix3.fromRotationY(Math.toRadians(90));
Matrix3.multiply(rotationMatrix, rot90, rotationMatrix);
const quaternion = Quaternion.fromRotationMatrix(rotationMatrix);
self.current.coordinateBall[id].rings['ring' + key].entity!.orientation = new ConstantProperty(quaternion);
const square = self.current.coordinateBall[id].squares['square' + key];
if (square) {
square.position = square.basePosition.clone();
square.entity!.orientation = new ConstantProperty(
Transforms.headingPitchRollQuaternion(centerPosition, new HeadingPitchRoll())
);
}
});
};
传入通过createCoordinateBall
返回的id,可以重置对应三维球的三维姿态。
设置三维球控制器的偏移参数
ts
const setCoordinateBallLines = (id: string, key: string, diffValue: number) => {
const coordinateBall = self.current.coordinateBall[id];
const lines = coordinateBall.lines;
const baseCartesian3 = lines[key].baseCartesian3;
const diffCartesian3 = coordinateBall.fixedDiffCartesian3;
Cartesian3.add(
diffCartesian3,
Cartesian3.multiplyByScalar(baseCartesian3, diffValue, new Cartesian3()),
diffCartesian3
);
lines[key].diffValue += diffValue;
};
设置三维球的显隐
ts
const setCoordinateBallShow = (id: string, show: boolean) => {
forEach(self.current.coordinateBall[id].balls, (value) => {
if (value.entity) {
value.entity.show = show;
}
});
forEach(self.current.coordinateBall[id].lines, (value) => {
if (value.entity) {
value.entity.show = show;
}
});
forEach(self.current.coordinateBall[id].squares, (value) => {
if (value.entity) {
value.entity.show = show;
}
});
forEach(self.current.coordinateBall[id].rings, (value) => {
if (value.entity) {
value.entity.show = show;
}
});
};
剖切功能代码介绍
这里再次回到本文的剖切功能这里。
剖切功能初始化
ts
self.current.clippingPlanes = new ClippingPlaneCollection({
planes: [new ClippingPlane(new Cartesian3(0.0, 0.0, -1.0), 0.0)],
unionClippingRegions: true,
enabled: false,
});
tileset.clippingPlanes = self.current.clippingPlanes;
const boundingSphere = tileset.boundingSphere;
const radius = boundingSphere.radius;
Cartesian3.add(boundingSphere.center, new Cartesian3(), self.current.diffDistance);
self.current.planeEntity = viewer.entities.add({
position: new CallbackProperty(() => {
return self.current.diffDistance;
}, false) as unknown as Cartesian3,
plane: {
dimensions: new Cartesian2(radius * 1.2, radius * 1.2),
material: Color.WHITE.withAlpha(0.5),
plane: new ClippingPlane(new Cartesian3(0.0, 0.0, 1.0), 0.0),
outline: true,
outlineColor: Color.WHITE,
fill: true,
},
});
使用cesium原生apinew ClippingPlaneCollection
创建真实的剖切平面,然后再手动绘制一个可见的平面self.current.planeEntity
,因为平面会随时更改方位,所以这里用变量self.current.diffDistance
设置在position: new CallbackProperty
中,这样改变变量就能改变平面位置。
绑定三维球控制器和剖切平面
ts
const { id, emitter } = createCoordinateBall({
coordinate: {
longitude: cesiumMath.toDegrees(longitude),
latitude: cesiumMath.toDegrees(latitude),
height: carHeight,
},
shows: [
{ line: true, ball: true, ring: true, square: true },
{ line: true, ball: true, ring: true, square: true },
{ line: true, ball: true, ring: true, square: true },
],
zoomShow: false,
squareMove: false,
initialQuaternion: !!self.current.pickState ? initialQuaternion : undefined,
});
self.current.coordinateBallId = id;
通过三维球箭头控制平面位移
ts
if (data.name.includes('line')) {
Cartesian3.add(boundingSphere.center, data.diffCartesian3, self.current.diffDistance);
if (self.current.clippingPlanes) {
self.current.clippingPlanes.modelMatrix = computeModelMatrix();
}
}
通过三维球小球控制平面旋转
ts
if (data.name.includes('ball')) {
const [_, key]: [any, 'X' | 'Y' | 'Z'] = data.name.split('ball');
const diffAngle = data.angle - self.current.angle[key];
const quaternionEntity = Quaternion.fromAxisAngle(
self.current.axisPosition[key],
cesiumMath.toRadians(diffAngle)
);
Quaternion.multiply(self.current.quaternion, quaternionEntity, self.current.quaternion);
self.current.planeEntity!.orientation = new ConstantProperty(self.current.quaternion);
if (self.current.pickState) {
const modelMatrix = self.current.planeEntity!.computeModelMatrix(new JulianDate());
const vecZ = Matrix4.multiplyByPoint(modelMatrix, new Cartesian3(0, 0, 1), new Cartesian3());
const normalVec = Cartesian3.normalize(
Cartesian3.subtract(vecZ, self.current.diffDistance, new Cartesian3()),
new Cartesian3()
);
const worldMatrix = Transforms.eastNorthUpToFixedFrame(self.current.diffDistance);
const worldToLocalMatrix = Matrix4.inverse(worldMatrix, new Matrix4());
const c3 = Cartesian3.normalize(
Matrix4.multiplyByPoint(
worldToLocalMatrix,
Cartesian3.add(self.current.diffDistance, normalVec, new Cartesian3()),
new Cartesian3()
),
new Cartesian3()
);
self.current.clippingPlanes!.get(0).normal = Cartesian3.multiplyByScalar(c3, -1, new Cartesian3());
} else {
const quaternionClipping = Quaternion.fromAxisAngle(
self.current.axisCliPosition[key],
cesiumMath.toRadians(diffAngle)
);
Quaternion.multiply(self.current.clippingQuaternion, quaternionClipping, self.current.clippingQuaternion);
}
self.current.angle[key] = data.angle;
if (self.current.clippingPlanes) {
self.current.clippingPlanes.modelMatrix = computeModelMatrix();
}
}
通过三维球方块控制平面归位
ts
if (data.type == 'clickSquare') {
self.current.pickState = undefined;
const [_, key]: [any, 'X' | 'Y' | 'Z'] = data.name.split('square');
resetCoordinateBall(id);
Cartesian3.add(boundingSphere.center, new Cartesian3(0, 0, 0), self.current.diffDistance);
self.current.angle = {
X: 0,
Y: 0,
Z: 0,
};
self.current.clippingQuaternion = Quaternion.IDENTITY.clone();
let planeNormalVec = new Cartesian3();
if (key === 'X') {
planeNormalVec = new Cartesian3(-1, 0, 0);
} else if (key === 'Y') {
planeNormalVec = new Cartesian3(0, 1, 0);
} else if (key === 'Z') {
planeNormalVec = new Cartesian3(0, 0, -1);
}
computeAxisAndPlaneQuaternion(key);
if (self.current.planeEntity && self.current.clippingPlanes) {
self.current.planeEntity.orientation = new ConstantProperty(self.current.quaternion);
self.current.clippingPlanes.get(0).normal = planeNormalVec;
self.current.clippingPlanes.modelMatrix = computeModelMatrix();
}
}
结束剖切
销毁三维球控制器,销毁剖切平面,以及重置代码里的各个参数
ts
const stopAxialDeepCut = () => {
if (self.current.planeEntity) {
removeCoordinateBall();
viewer.entities.remove(self.current.planeEntity);
if (self.current.tileset && self.current.tileset.clippingPlanes && self.current.clippingPlanes) {
self.current.clippingPlanes.enabled = false;
self.current.clippingPlanes.modelMatrix = Matrix4.fromTranslationQuaternionRotationScale(
new Cartesian3(10000000, 10000000, 10000000),
Quaternion.IDENTITY,
new Cartesian3(0.000001, 0.000001, 0.000001)
);
self.current.clippingPlanes = undefined;
}
self.current = {
diffDistance: new Cartesian3(),
distanceX: 0,
distanceY: 0,
distanceZ: 0,
rotateMatrix: Matrix4.clone(Matrix4.IDENTITY),
angle: {
X: 0,
Y: 0,
Z: 0,
},
axisPosition: {
X: new Cartesian3(0, 1, 0),
Y: new Cartesian3(1, 0, 0),
Z: new Cartesian3(0, 0, 1),
},
axisCliPosition: {
X: new Cartesian3(1, 0, 0),
Y: new Cartesian3(0, -1, 0),
Z: new Cartesian3(0, 0, 1),
},
p0: new Cartesian3(1, 0, 0),
normalVec: new Cartesian3(1, 0, 0),
quaternion: Quaternion.IDENTITY.clone(),
clippingQuaternion: Quaternion.IDENTITY.clone(),
baseComputeModelMatrix: Matrix4.IDENTITY.clone(),
pickState: undefined,
deepCutToolShow: false,
};
} else {
fatal('`stopAxialDeepCut` 还未开始进行轴向剖切!');
return;
}
};
是否显示剖切工具
ts
const showAxialDeepCutTool = (show: boolean) => {
if (!self.current.planeEntity) {
fatal('`showAxialDeepCutTool` 还未开始进行轴向剖切!');
return;
}
if (self.current.deepCutToolShow != show) {
self.current.planeEntity.show = show;
self.current.coordinateBallId && setCoordinateBallShow(self.current.coordinateBallId, show);
self.current.deepCutToolShow = show;
}
};
结语
这个功能涉及到的模块多,代码量多,而且计算也非常多,还有录屏里的吸附功能模块等,暂时还未一一介绍。