实现 dwg 图纸的 3d 标注功能;云线标注,矩形标注,圆形标注,剪头标注,文字标注。
代码架构
基类封装
分析需求后可知,绘制交互、绘制结果,基本类似可复用,所以设计一个标记操作基类、标记结果基类,有差别的交互细节可在子类中单独实现
// OpAnnotationDwg.ts
import {
Plane,
Raycaster,
Vector3,
Vector2,
Mesh,
Scene,
Object3D,
Font,
LineBasicMaterial,
Material,
CircleBufferGeometry,
} from "../../WebViewer/THREE/Three";
// @ts-ignore
import { AutoTransform } from "../../WebViewer/Core/AutoTransform.js";
let currentLeft: number;
let currentTop: number;
let currentPageX: number;
let currentPageY: number;
let isMobileMove: boolean;
let isDownMove: boolean;
let _viewZ = new Vector3();
let _viewPlane = new Plane();
let _rayCaster = new Raycaster();
// @ts-ignore
_rayCaster.params.Line.threshold = 10;
class OpAnnotationDwg {
app: any;
private _onPointerDown: (event: any) => void;
private _onPointerMove: (event: any) => void;
private _onPointerUp: (event: any) => void;
private _onMouseZoom: (event: any) => void;
private _onKeyDown: (event: any) => void;
tempAnnotation: AnnotationMesh | null;
selectedAnnotation: AnnotationMesh | null;
editDragIndex: number;
annotationMeshArr: Array<AnnotationMesh>;
lineSize: number;
textSize: number;
color: string;
mouseSimulator: HTMLElement | null;
constructor(app: any) {
this.app = app;
this._onPointerDown = onPointerDown.bind(this);
this._onPointerMove = onPointerMove.bind(this);
this._onPointerUp = onPointerUp.bind(this);
this._onMouseZoom = onMouseZoom.bind(this);
this._onKeyDown = onKeyDown.bind(this);
this.tempAnnotation = null;
this.selectedAnnotation = null;
this.editDragIndex = -1;
this.annotationMeshArr = [];
this.lineSize = 2;
this.textSize = 500;
this.color = "#ff0000";
this.mouseSimulator = null;
}
initEvent() {
if (isMobile()) {
this.mouseSimulator = document.createElement("div");
this.mouseSimulator.setAttribute("class", "ccbim__mouseImg");
const html = `<div class="ccbim__mouseImg__imshar"></div><img src='' alt='模拟鼠标'/>`;
this.mouseSimulator.innerHTML = html;
this.app.parentElement.appendChild(this.mouseSimulator);
this.mouseSimulator.lastChild.src = "../../assets/images/mouse1.png";
this.mouseSimulator.addEventListener("touchstart", this._onPointerDown);
this.mouseSimulator.addEventListener("touchmove", this._onPointerMove);
this.mouseSimulator.addEventListener("touchend", this._onPointerUp);
} else {
this.app.bimViewer.dom.addEventListener(
"pointerdown",
this._onPointerDown
);
this.app.bimViewer.dom.addEventListener(
"pointermove",
this._onPointerMove
);
this.app.bimViewer.dom.addEventListener("pointerup", this._onPointerUp);
this.app.bimViewer.dom.addEventListener("wheel", this._onMouseZoom);
document.addEventListener("keydown", this._onKeyDown);
}
}
disposeEvent() {
if (isMobile()) {
if (this.mouseSimulator) {
this.mouseSimulator.removeEventListener(
"touchstart",
this._onPointerDown
);
this.mouseSimulator.removeEventListener(
"touchmove",
this._onPointerMove
);
this.mouseSimulator.removeEventListener("touchend", this._onPointerUp);
this.app.parentElement.removeChild(this.mouseSimulator);
this.mouseSimulator = null;
}
} else {
this.app.bimViewer.dom.removeEventListener(
"pointerdown",
this._onPointerDown
);
this.app.bimViewer.dom.removeEventListener(
"pointermove",
this._onPointerMove
);
this.app.bimViewer.dom.removeEventListener(
"pointerup",
this._onPointerUp
);
}
this.stopAnnotation();
this.setSelectAnnotation(null);
}
private pointerDown(e: any) {
if (e.button === 0) e.preventDefault();
isMobileMove = false;
if (isMobile()) {
e.preventDefault();
if (this.mouseSimulator) {
currentLeft = this.mouseSimulator.offsetLeft;
currentTop = this.mouseSimulator.offsetTop;
this.mouseSimulator.lastChild.src = "../../assets/images/mouse2.png";
}
currentPageX = e.touches[0].pageX;
currentPageY = e.touches[0].pageY;
} else {
this.handleClick(e);
}
}
private pointerMove(e: any): void {
this.app.getViewer().cameraChanged();
let event: any = e;
if (event.buttons & 2 || event.buttons & 4) {
isDownMove = true;
}
if (isMobile()) {
e.preventDefault();
let eMovePageX = e.touches[0].pageX;
let eMovePageY = e.touches[0].pageY;
if (
Math.abs(eMovePageX - currentPageX) > 20 ||
Math.abs(eMovePageY - currentPageY) > 20
) {
isMobileMove = true;
}
let left = eMovePageX - currentPageX + currentLeft;
let top = eMovePageY - currentPageY + currentTop;
left < 0 && (left = 0);
top < 0 && (top = 0);
if (this.mouseSimulator && this.mouseSimulator.parentElement) {
let maxLeft =
this.mouseSimulator.parentElement.clientWidth -
this.mouseSimulator.clientWidth;
let maxTop =
this.mouseSimulator.parentElement.clientHeight -
this.mouseSimulator.clientHeight;
left > maxLeft && (left = maxLeft);
top > maxTop && (top = maxTop);
this.mouseSimulator.style.left = left + "px";
this.mouseSimulator.style.top = top + "px";
event = {
offsetX: left,
offsetY: top,
};
}
}
let point = this.getMouseWorldPos(event);
if (this.tempAnnotation) {
const index = this.tempAnnotation.ptsArr.length - 1;
const pixelScale2d = this.app.bimViewer.mainCamera.getPixelScale2d();
this.tempAnnotation.editPoint({ point, index, pixelScale2d });
return;
}
if (this.editDragIndex > -1) {
const index = this.editDragIndex;
const pixelScale2d = this.app.bimViewer.mainCamera.getPixelScale2d();
this.selectedAnnotation?.editPoint({ point, index, pixelScale2d });
return;
}
this.trySelectMeasure(event);
this.trySelectDrag(event);
}
private pointerUp(e: any): void {
if (isMobile()) {
e.preventDefault();
this.mouseSimulator.lastChild.src = "../../assets/images/mouse1.png";
this.handleClick(e);
}
}
private handleClick(e: PointerEvent) {
let event: any = e;
if (isMobile()) {
if (isMobileMove) return;
if (this.mouseSimulator) {
const left = this.mouseSimulator.offsetLeft;
const top = this.mouseSimulator.offsetTop;
event = {
offsetX: left,
offsetY: top,
};
}
}
if (isDownMove) {
isDownMove = false;
return;
}
let point = this.getMouseWorldPos(event);
if (this.tempAnnotation) {
this.tempAnnotationPointerUp({ event, point });
return;
}
const selectedAnnotation = this.trySelectMeasure(event);
if (selectedAnnotation) {
this.setSelectAnnotation(selectedAnnotation);
return;
}
const selectDrag = this.trySelectDrag(event);
if (selectDrag) {
if (this.editDragIndex === -1) {
this.editDragIndex = selectDrag.index;
} else {
this.editDragIndex = -1;
}
return;
}
if (event.button === 1 || event.button === 2) return;
const measure = this.createNewAnnotation(point);
if (measure) {
this.tempAnnotation = measure;
this.annotationMeshArr.push(measure);
}
this.setSelectAnnotation(null);
}
private mouseZoom(e: Event) {
const pixelScale2d = this.app.bimViewer.mainCamera.getPixelScale2d();
this.annotationMeshArr.forEach((item) => {
item.updateLineTextShape(pixelScale2d);
});
this.app.getViewer().cameraChanged();
}
private keyDown(e: KeyboardEvent) {
if (e.keyCode === 27) {
if (this.tempAnnotation) {
this.tempAnnotation.dispose();
this.tempAnnotation = null;
this.app.getViewer().cameraChanged();
}
}
}
tempAnnotationPointerUp(param: { event: PointerEvent; point: Vector3 }) {}
createNewAnnotation(point: Vector3): any {}
stopAnnotation() {
this.setSelectAnnotation(this.tempAnnotation);
this.tempAnnotation = null;
}
trySelectMeasure(e: PointerEvent) {
this.getMouseRaycaster(e, _rayCaster);
let measure = null;
this.annotationMeshArr.forEach((item) => {
if (item !== this.selectedAnnotation) {
const interse = item.intersectObjects(_rayCaster);
if (interse) {
measure = item;
item.changeMeshMaterial(item.hoverMaterial);
} else {
item.changeMeshMaterial(item.material);
}
} else {
item.changeMeshMaterial(item.material);
}
});
return measure;
}
trySelectDrag(e: PointerEvent): any {
this.getMouseRaycaster(e, _rayCaster);
let drag = null;
this.annotationMeshArr.forEach((item) => {
if (item === this.selectedAnnotation) {
const interse = item.intersectDrag(_rayCaster);
drag = interse;
}
});
return drag;
}
setSelectAnnotation(mesh: AnnotationMesh | null) {
this.annotationMeshArr.forEach((item) => {
item.setDragHandleVisible(false);
});
if (mesh) {
this.selectedAnnotation = mesh;
this.selectedAnnotation.setDragHandleVisible(true);
} else {
this.selectedAnnotation = null;
}
this.app.getViewer().cameraChanged();
}
deleteSelectAnnotation() {
if (!this.selectedAnnotation) return;
for (let i = 0, il = this.annotationMeshArr.length; i < il; ++i) {
if (this.annotationMeshArr[i] === this.selectedAnnotation) {
this.annotationMeshArr.splice(i, 1);
break;
}
}
this.selectedAnnotation.dispose();
this.selectedAnnotation = null;
}
setAnnotationMeshArrVisible(value: boolean) {
this.annotationMeshArr.forEach((item) => {
item.setMeshVisible(value);
});
}
deleteAllAnnotation() {
this.stopAnnotation();
this.annotationMeshArr.forEach((item) => {
item.dispose();
});
this.annotationMeshArr = [];
}
setNextLineSize(val: number) {
this.lineSize = val;
}
setNextTextSize(val: number) {
this.textSize = val;
}
setNextColor(color: string) {
this.color = color;
}
getAnnotationData() {
let data: any = [];
this.annotationMeshArr.forEach((item) => {
const d = item.getData();
data.push(d);
});
return data;
}
setAnnotationData(data: Array<any>) {
data.forEach((item) => {
const measure = this.createAnnotationFromData(item);
this.annotationMeshArr.push(measure);
});
}
createAnnotationFromData(data: any): any {}
dispose() {
this.disposeEvent();
this.deleteAllAnnotation();
}
getMouseWorldPos(e: PointerEvent) {
const camera = this.app.bimViewer.getMainCamera();
_viewZ.subVectors(camera.position, camera.target).normalize();
_viewPlane.setFromNormalAndCoplanarPoint(_viewZ, camera.target);
this.getMouseRaycaster(e, _rayCaster);
let target = new Vector3();
_rayCaster.ray.intersectPlane(_viewPlane, target);
return target;
}
getMouseRaycaster(e: PointerEvent, rayster: Raycaster) {
const _windowPos = this.getMouseWindowPos(e);
const camera = this.app.bimViewer.getMainCamera();
rayster.far = Infinity;
rayster.near = camera.near;
rayster.setFromCamera(_windowPos, camera);
}
getMouseWindowPos(e: PointerEvent) {
const renderDomRect = this.app.bimViewer
.getRenderDom()
.getBoundingClientRect();
let windowCoordinate = new Vector2();
windowCoordinate.set(
(e.offsetX / renderDomRect.width) * 2 - 1,
(e.offsetY / renderDomRect.height) * -2 + 1
);
return windowCoordinate;
}
}
function onPointerDown(this: any, e: PointerEvent) {
this.pointerDown(e);
}
function onPointerMove(this: any, e: PointerEvent) {
this.pointerMove(e);
}
function onPointerUp(this: any, e: PointerEvent) {
this.pointerUp(e);
}
function onMouseZoom(this: any, e: Event) {
this.mouseZoom(e);
}
function onKeyDown(this: any, e: Event) {
this.keyDown(e);
}
class AnnotationMesh {
ptsArr: Array<Vector3>;
lineShape!: Mesh;
dragHandleArr: Array<DragHadle>;
material: LineBasicMaterial;
hoverMaterial: LineBasicMaterial;
scene: Scene;
font: Font;
lineSize: number;
textSize: number;
color: string;
constructor(param: {
start: Vector3;
end: Vector3;
font: Font;
scene: Scene;
lineSize: number;
textSize: number;
color: string;
}) {
this.material = new LineBasicMaterial({ color: param.color });
this.hoverMaterial = new LineBasicMaterial({ color: 0xffff00 });
this.scene = param.scene;
this.font = param.font;
this.ptsArr = [param.start.clone(), param.end.clone()];
this.dragHandleArr = [];
this.lineSize = param.lineSize;
this.textSize = param.textSize;
this.color = param.color;
}
addPoint(point: Vector3) {
this.ptsArr.push(point);
const drag = this.createDragHandle(point);
this.dragHandleArr.push(drag);
drag.install(this.scene);
}
editPoint(param: { point: Vector3; index: number; pixelScale2d: number }) {
this.ptsArr[param.index].copy(param.point);
this.updateLineTextShape(param.pixelScale2d);
this.updateDragShape(param.index - 1);
}
updateLineTextShape(pixelScale2d: number) {}
updateDragShape(current: number) {
this.dragHandleArr[current + 1].setPosition(this.ptsArr[current + 1]);
}
intersectObjects(_rayCaster: Raycaster) {
let children: any = [];
children.push(this.lineShape);
const intersects = _rayCaster.intersectObjects(children, true);
if (intersects.length > 0) {
return true;
}
return false;
}
intersectDrag(_rayCaster: Raycaster) {
for (let i = 0; i < this.dragHandleArr.length; i++) {
const obj = this.dragHandleArr[i].autoTransform.children[0];
const intersect = _rayCaster.intersectObject(obj, false);
if (intersect.length > 0) {
obj.material = this.hoverMaterial;
return {
obj: this.dragHandleArr[i],
index: i,
};
} else {
obj.material = this.material;
}
}
return false;
}
changeMeshMaterial(material: Material) {
if (this.lineShape.material === material) {
return;
}
this.lineShape.material = material;
}
updateText() {}
createDragHandle(point: Vector3) {
const dragHandle = new DragHadle();
dragHandle.setAutoScale(true);
dragHandle.setAlignToScreen(true);
const circle = new CircleBufferGeometry(6);
dragHandle.add(new Mesh(circle, this.material));
dragHandle.setPosition(point);
dragHandle.setVisible(false);
return dragHandle;
}
setDragHandleVisible(value: boolean) {
this.dragHandleArr.forEach((item) => {
item.setVisible(value);
});
}
setMeshVisible(value: boolean) {
if (value === false) {
this.dragHandleArr.forEach((item) => {
item.setVisible(value);
});
}
this.lineShape.visible = value;
}
getData() {
let pointArr: any = [];
this.ptsArr.forEach((item) => {
const arr = item.toArray();
pointArr.push(arr);
});
let obj = {
ptsArr: pointArr,
lineSize: this.lineSize,
textSize: this.textSize,
color: this.color,
};
return obj;
}
setData() {}
dispose() {
this.dragHandleArr.forEach((item) => {
item.unInstall(this.scene);
});
this.dragHandleArr = [];
this.scene.remove(this.lineShape);
}
}
class Handle {
autoTransform: AutoTransform;
constructor() {
this.autoTransform = new AutoTransform();
}
install(scene: Scene) {
scene.add(this.autoTransform);
}
unInstall(scene: Scene) {
this.autoTransform.removeAll();
scene.remove(this.autoTransform);
}
setAutoScale(value: boolean) {
this.autoTransform.setAutoScale(value);
}
setAlignToScreen(value: boolean) {
this.autoTransform.setAlignToScreen(value);
}
setPosition(value: Vector3) {
this.autoTransform.setPosition(value);
}
getPosition() {
return this.autoTransform.getPosition();
}
setVisible(value: boolean) {
this.autoTransform.visible = value;
}
add(mesh: Object3D) {
this.autoTransform.add(mesh);
}
removeAll() {
this.autoTransform.removeAll();
}
}
class DragHadle extends Handle {
constructor() {
super();
}
}
function isMobile() {
var userAgent = navigator.userAgent;
return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(
userAgent
);
}
export { OpAnnotationDwg, AnnotationMesh, DragHadle };
子类实现
不同的功能,交互细节上有点差异,绘制的几何形状也有差别,在基类中抽离公共的部分后,在子类里实现具体的功能
云线框
核心算法在于云线数据如何生成
// OpAnnotationDwgCloud.ts
import {
Vector3,
Font,
Scene,
Shape,
ShapeGeometry,
Mesh,
Path,
Vector2,
} from "../../WebViewer/THREE/Three";
import { AnnotationMesh, OpAnnotationDwg } from "./OpAnnotationDwg";
class OpAnnotationDwgCloud extends OpAnnotationDwg {
constructor(app: any) {
super(app);
}
tempAnnotationPointerUp() {
if (!this.tempAnnotation) return;
const length = this.tempAnnotation.ptsArr.length - 1;
let start = this.tempAnnotation.ptsArr[length - 1].clone();
let end = this.tempAnnotation.ptsArr[length].clone();
let distance = start.distanceTo(end);
if (distance === 0) return;
super.stopAnnotation();
}
createNewAnnotation(point: Vector3) {
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationCloudMesh({
start: point,
end: point,
font,
scene,
lineSize: this.lineSize,
textSize: this.textSize,
color: this.color,
});
return mesh;
}
createAnnotationFromData(data: {
ptsArr: Array<number[]>;
lineSize: number;
textSize: number;
color: string;
}) {
let pointArr: Vector3[] = [];
data.ptsArr.forEach((item: number[]) => {
const v3 = new Vector3().fromArray(item);
pointArr.push(v3);
});
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationCloudMesh({
start: pointArr[0],
end: pointArr[1],
font,
scene,
lineSize: data.lineSize,
textSize: data.textSize,
color: data.color,
});
const pixelScale2d = this.app.bimViewer.mainCamera.getPixelScale2d();
mesh.updateLineTextShape(pixelScale2d);
mesh.setDragHandleVisible(false);
return mesh;
}
}
class AnnotationCloudMesh extends AnnotationMesh {
constructor(param: {
start: Vector3;
end: Vector3;
font: Font;
scene: Scene;
lineSize: number;
textSize: number;
color: string;
}) {
super(param);
this._createLineTextShape();
}
private _createLineTextShape() {
const shape = new Shape();
const shapeGeo = new ShapeGeometry(shape);
this.lineShape = new Mesh(shapeGeo, this.material);
this.scene.add(this.lineShape);
this.ptsArr.forEach((point) => {
const drag = this.createDragHandle(point);
this.dragHandleArr.push(drag);
drag.install(this.scene);
});
}
updateLineTextShape(pixelScale2d: number) {
pixelScale2d = pixelScale2d * (this.lineSize + 1);
if (pixelScale2d < 0.00099) pixelScale2d = 0.00099;
const startP = new Vector2(
Math.min(this.ptsArr[0].x, this.ptsArr[1].x),
Math.max(this.ptsArr[0].y, this.ptsArr[1].y)
);
const endP = new Vector2(
Math.max(this.ptsArr[0].x, this.ptsArr[1].x),
Math.min(this.ptsArr[0].y, this.ptsArr[1].y)
);
let width = Math.abs(endP.x - startP.x);
let height = Math.abs(endP.y - startP.y);
if (width === 0 || height === 0) return;
const lenW = width / 10;
const lenH = height / 10;
const len = Math.max(lenW, lenH);
const lenHalf = len / 2;
const segmentW = Math.ceil(width / len);
const segmentH = Math.ceil(height / len);
const LT = startP;
const RT = new Vector2(endP.x, startP.y);
const RB = endP;
const LB = new Vector2(startP.x, endP.y);
const arr1 = divideLineSegment(LT, RT, segmentW);
const arr2 = divideLineSegment(RT, RB, segmentH);
const arr3 = divideLineSegment(RB, LB, segmentW);
const arr4 = divideLineSegment(LB, LT, segmentH);
const shape = new Shape();
shape.moveTo(startP.x, startP.y);
for (let i = 0; i < arr1.length - 1; i++) {
shape.bezierCurveTo(
arr1[i].x,
arr1[i].y,
arr1[i].x + lenHalf,
arr1[i].y + lenHalf,
arr1[i + 1].x,
arr1[i + 1].y
);
}
for (let i = 0; i < arr2.length - 1; i++) {
shape.bezierCurveTo(
arr2[i].x,
arr2[i].y,
arr2[i].x + lenHalf,
arr2[i].y - lenHalf,
arr2[i + 1].x,
arr2[i + 1].y
);
}
for (let i = 0; i < arr3.length - 1; i++) {
shape.bezierCurveTo(
arr3[i].x,
arr3[i].y,
arr3[i].x - lenHalf,
arr3[i].y - lenHalf,
arr3[i + 1].x,
arr3[i + 1].y
);
}
for (let i = 0; i < arr4.length - 1; i++) {
shape.bezierCurveTo(
arr4[i].x,
arr4[i].y,
arr4[i].x - lenHalf,
arr4[i].y + lenHalf,
arr4[i + 1].x,
arr4[i + 1].y
);
}
shape.closePath();
const holePath = new Path();
holePath.moveTo(startP.x, startP.y - pixelScale2d);
for (let i = 0; i < arr1.length - 1; i++) {
holePath.bezierCurveTo(
arr1[i].x,
arr1[i].y - pixelScale2d,
arr1[i].x + lenHalf,
arr1[i].y + lenHalf - pixelScale2d,
arr1[i + 1].x,
arr1[i + 1].y - pixelScale2d
);
}
for (let i = 0; i < arr2.length - 1; i++) {
holePath.bezierCurveTo(
arr2[i].x - pixelScale2d,
arr2[i].y,
arr2[i].x + lenHalf - pixelScale2d,
arr2[i].y - lenHalf,
arr2[i + 1].x - pixelScale2d,
arr2[i + 1].y
);
}
for (let i = 0; i < arr3.length - 1; i++) {
holePath.bezierCurveTo(
arr3[i].x,
arr3[i].y + pixelScale2d,
arr3[i].x - lenHalf,
arr3[i].y - lenHalf + pixelScale2d,
arr3[i + 1].x,
arr3[i + 1].y + pixelScale2d
);
}
for (let i = 0; i < arr4.length - 1; i++) {
holePath.bezierCurveTo(
arr4[i].x + pixelScale2d,
arr4[i].y,
arr4[i].x - lenHalf + pixelScale2d,
arr4[i].y + lenHalf,
arr4[i + 1].x + pixelScale2d,
arr4[i + 1].y
);
}
holePath.closePath();
shape.holes.push(holePath);
const shapeGeo = new ShapeGeometry(shape);
this.lineShape.geometry.dispose();
this.lineShape.geometry = shapeGeo;
}
}
function divideLineSegment(start: Vector2, end: Vector2, n: number) {
const points = [];
const x1 = start.x;
const y1 = start.y;
const x2 = end.x;
const y2 = end.y;
for (let i = 0; i <= n; i++) {
const t = i / n;
const x = x1 + t * (x2 - x1);
const y = y1 + t * (y2 - y1);
points.push({ x, y });
}
return points;
}
export { OpAnnotationDwgCloud };
矩形框
// OpAnnotationDwgRect.ts
import {
Vector3,
Font,
Scene,
Shape,
ShapeGeometry,
Mesh,
Path,
Vector2,
} from "../../WebViewer/THREE/Three";
import { AnnotationMesh, OpAnnotationDwg } from "./OpAnnotationDwg";
class OpAnnotationDwgRect extends OpAnnotationDwg {
constructor(app: any) {
super(app);
}
tempAnnotationPointerUp() {
if (!this.tempAnnotation) return;
const length = this.tempAnnotation.ptsArr.length - 1;
let start = this.tempAnnotation.ptsArr[length - 1].clone();
let end = this.tempAnnotation.ptsArr[length].clone();
let distance = start.distanceTo(end);
if (distance === 0) return;
super.stopAnnotation();
}
createNewAnnotation(point: Vector3) {
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationRectMesh({
start: point,
end: point,
font,
scene,
lineSize: this.lineSize,
textSize: this.textSize,
color: this.color,
});
return mesh;
}
createAnnotationFromData(data: {
ptsArr: Array<number[]>;
lineSize: number;
textSize: number;
color: string;
}) {
let pointArr: Vector3[] = [];
data.ptsArr.forEach((item: number[]) => {
const v3 = new Vector3().fromArray(item);
pointArr.push(v3);
});
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationRectMesh({
start: pointArr[0],
end: pointArr[1],
font,
scene,
lineSize: data.lineSize,
textSize: data.textSize,
color: data.color,
});
const pixelScale2d = this.app.bimViewer.mainCamera.getPixelScale2d();
mesh.updateLineTextShape(pixelScale2d);
mesh.setDragHandleVisible(false);
return mesh;
}
}
class AnnotationRectMesh extends AnnotationMesh {
constructor(param: {
start: Vector3;
end: Vector3;
font: Font;
scene: Scene;
lineSize: number;
textSize: number;
color: string;
}) {
super(param);
this._createLineTextShape();
}
private _createLineTextShape() {
const shape = new Shape();
const shapeGeo = new ShapeGeometry(shape);
this.lineShape = new Mesh(shapeGeo, this.material);
this.scene.add(this.lineShape);
this.ptsArr.forEach((point) => {
const drag = this.createDragHandle(point);
this.dragHandleArr.push(drag);
drag.install(this.scene);
});
}
updateLineTextShape(pixelScale2d: number) {
pixelScale2d = pixelScale2d * (this.lineSize + 1);
if (pixelScale2d < 0.00099) pixelScale2d = 0.00099;
const startP = new Vector2(
Math.min(this.ptsArr[0].x, this.ptsArr[1].x),
Math.max(this.ptsArr[0].y, this.ptsArr[1].y)
);
const endP = new Vector2(
Math.max(this.ptsArr[0].x, this.ptsArr[1].x),
Math.min(this.ptsArr[0].y, this.ptsArr[1].y)
);
const shape = new Shape();
shape.moveTo(startP.x, startP.y);
shape.lineTo(endP.x, startP.y);
shape.lineTo(endP.x, endP.y);
shape.lineTo(startP.x, endP.y);
shape.closePath();
const holePath = new Path();
holePath.moveTo(startP.x + pixelScale2d, startP.y - pixelScale2d);
holePath.lineTo(endP.x - pixelScale2d, startP.y - pixelScale2d);
holePath.lineTo(endP.x - pixelScale2d, endP.y + pixelScale2d);
holePath.lineTo(startP.x + pixelScale2d, endP.y + pixelScale2d);
holePath.closePath();
shape.holes.push(holePath);
const shapeGeo = new ShapeGeometry(shape);
this.lineShape.geometry.dispose();
this.lineShape.geometry = shapeGeo;
}
}
export { OpAnnotationDwgRect };
圆形框
// OpAnnotationDwgCircle
import {
Vector3,
Font,
Scene,
Shape,
ShapeGeometry,
Mesh,
Path,
Vector2,
} from "../../WebViewer/THREE/Three";
import { AnnotationMesh, OpAnnotationDwg } from "./OpAnnotationDwg";
class OpAnnotationDwgCircle extends OpAnnotationDwg {
constructor(app: any) {
super(app);
}
tempAnnotationPointerUp() {
if (!this.tempAnnotation) return;
const length = this.tempAnnotation.ptsArr.length - 1;
let start = this.tempAnnotation.ptsArr[length - 1].clone();
let end = this.tempAnnotation.ptsArr[length].clone();
let distance = start.distanceTo(end);
if (distance === 0) return;
super.stopAnnotation();
}
createNewAnnotation(point: Vector3) {
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationCircleMesh({
start: point,
end: point,
font,
scene,
lineSize: this.lineSize,
textSize: this.textSize,
color: this.color,
});
return mesh;
}
createAnnotationFromData(data: {
ptsArr: Array<number[]>;
lineSize: number;
textSize: number;
color: string;
}) {
let pointArr: Vector3[] = [];
data.ptsArr.forEach((item: number[]) => {
const v3 = new Vector3().fromArray(item);
pointArr.push(v3);
});
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationCircleMesh({
start: pointArr[0],
end: pointArr[1],
font,
scene,
lineSize: data.lineSize,
textSize: data.textSize,
color: data.color,
});
const pixelScale2d = this.app.bimViewer.mainCamera.getPixelScale2d();
mesh.updateLineTextShape(pixelScale2d);
mesh.setDragHandleVisible(false);
return mesh;
}
}
class AnnotationCircleMesh extends AnnotationMesh {
constructor(param: {
start: Vector3;
end: Vector3;
font: Font;
scene: Scene;
lineSize: number;
textSize: number;
color: string;
}) {
super(param);
this._createLineTextShape();
}
private _createLineTextShape() {
const shape = new Shape();
const shapeGeo = new ShapeGeometry(shape);
this.lineShape = new Mesh(shapeGeo, this.material);
this.scene.add(this.lineShape);
this.ptsArr.forEach((point) => {
const drag = this.createDragHandle(point);
this.dragHandleArr.push(drag);
drag.install(this.scene);
});
}
updateLineTextShape(pixelScale2d: number) {
pixelScale2d = pixelScale2d * (this.lineSize + 1);
if (pixelScale2d < 0.00099) pixelScale2d = 0.00099;
const startP = new Vector2(
Math.min(this.ptsArr[0].x, this.ptsArr[1].x),
Math.max(this.ptsArr[0].y, this.ptsArr[1].y)
);
const endP = new Vector2(
Math.max(this.ptsArr[0].x, this.ptsArr[1].x),
Math.min(this.ptsArr[0].y, this.ptsArr[1].y)
);
const centerX = (startP.x + endP.x) / 2;
const centerY = (startP.y + endP.y) / 2;
const a = Math.abs((endP.x - startP.x) / 2);
const b = Math.abs((endP.y - startP.y) / 2);
const shape = new Shape();
for (let i = 0; i <= 2 * Math.PI; i += 0.01) {
const x = a * Math.cos(i);
const y = b * Math.sin(i);
if (i === 0) {
shape.moveTo(x + centerX, y + centerY);
} else {
shape.lineTo(x + centerX, y + centerY);
}
}
shape.closePath();
const holePath = new Path();
for (let i = 0; i <= 2 * Math.PI; i += 0.01) {
const x = (a - pixelScale2d) * Math.cos(i);
const y = (b - pixelScale2d) * Math.sin(i);
if (i === 0) {
holePath.moveTo(x + centerX, y + centerY);
} else {
holePath.lineTo(x + centerX, y + centerY);
}
}
holePath.closePath();
shape.holes.push(holePath);
const shapeGeo = new ShapeGeometry(shape);
this.lineShape.geometry.dispose();
this.lineShape.geometry = shapeGeo;
}
}
export { OpAnnotationDwgCircle };
箭头(引线文字标注)
核心算法在于箭头数据如何生成
// OpAnnotationDwgLead.ts
import {
Vector3,
Font,
Scene,
Shape,
ShapeGeometry,
Mesh,
TextGeometry,
Raycaster,
Material,
} from "../../WebViewer/THREE/Three";
import { AnnotationMesh, OpAnnotationDwg } from "./OpAnnotationDwg";
class OpAnnotationDwgLead extends OpAnnotationDwg {
finishcallback: Function;
constructor(app: any) {
super(app);
this.finishcallback = () => {};
}
bindFinishCallback(callback: Function) {
if (callback instanceof Function) {
this.finishcallback = callback;
}
}
tempAnnotationPointerUp() {
if (!this.tempAnnotation) return;
const length = this.tempAnnotation.ptsArr.length - 1;
let start = this.tempAnnotation.ptsArr[length - 1].clone();
let end = this.tempAnnotation.ptsArr[length].clone();
let distance = start.distanceTo(end);
if (distance === 0) return;
super.stopAnnotation();
this.finishcallback();
}
createNewAnnotation(point: Vector3) {
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationLeadMesh({
start: point,
end: point,
font,
scene,
lineSize: this.lineSize,
textSize: this.textSize,
color: this.color,
});
return mesh;
}
createAnnotationFromData(data: {
ptsArr: Array<number[]>;
lineSize: number;
textSize: number;
color: string;
text: string;
}) {
let pointArr: Vector3[] = [];
data.ptsArr.forEach((item: number[]) => {
const v3 = new Vector3().fromArray(item);
pointArr.push(v3);
});
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationLeadMesh({
start: pointArr[0],
end: pointArr[1],
font,
scene,
lineSize: data.lineSize,
textSize: data.textSize,
color: data.color,
});
mesh.createLeadText(data.text);
const pixelScale2d = this.app.bimViewer.mainCamera.getPixelScale2d();
mesh.updateLineTextShape(pixelScale2d);
mesh.setDragHandleVisible(false);
return mesh;
}
}
class AnnotationLeadMesh extends AnnotationMesh {
textMesh!: Mesh;
text: string;
constructor(param: {
start: Vector3;
end: Vector3;
font: Font;
scene: Scene;
lineSize: number;
textSize: number;
color: string;
}) {
super(param);
this.text = "";
this._createLineTextShape();
}
private _createLineTextShape() {
const shape = new Shape();
const shapeGeo = new ShapeGeometry(shape);
this.lineShape = new Mesh(shapeGeo, this.material);
this.scene.add(this.lineShape);
this.ptsArr.forEach((point) => {
const drag = this.createDragHandle(point);
this.dragHandleArr.push(drag);
drag.install(this.scene);
});
}
createLeadText(text: string) {
this.text = text;
const textGeo = new TextGeometry(text, {
font: this.font,
size: this.textSize,
height: 0.001,
curveSegments: 10,
});
this.textMesh = new Mesh(textGeo, this.material);
this.textMesh.geometry.computeBoundingBox();
this.scene.add(this.textMesh);
this.textMesh.position.copy(this.ptsArr[1]);
}
updateLineTextShape(pixelScale2d: number) {
pixelScale2d = pixelScale2d * 0.5 * (this.lineSize + 1);
if (pixelScale2d < 0.00099) pixelScale2d = 0.00099;
const direction = this.ptsArr[1]
.clone()
.sub(this.ptsArr[0])
.normalize()
.multiplyScalar(pixelScale2d);
const normal = new Vector3(0, 0, 1)
.cross(direction)
.normalize()
.multiplyScalar(pixelScale2d);
const normal2 = normal.clone().multiplyScalar(5);
const direction2 = direction.clone().multiplyScalar(12);
if (
this.ptsArr[0].distanceTo(this.ptsArr[1]) <
this.ptsArr[0].distanceTo(
new Vector3().copy(this.ptsArr[0]).add(direction2)
)
) {
return;
}
const l1 = new Vector3().copy(this.ptsArr[0]);
const l2 = new Vector3().copy(this.ptsArr[0]).add(direction2).sub(normal2);
const l3 = new Vector3().copy(this.ptsArr[0]).add(direction2).sub(normal);
const l4 = new Vector3().copy(this.ptsArr[1]).sub(normal);
const l5 = new Vector3().copy(this.ptsArr[1]).add(normal);
const l6 = new Vector3().copy(this.ptsArr[0]).add(direction2).add(normal);
const l7 = new Vector3().copy(this.ptsArr[0]).add(direction2).add(normal2);
const shape = new Shape();
shape.moveTo(l1.x, l1.y);
shape.lineTo(l2.x, l2.y);
shape.lineTo(l3.x, l3.y);
shape.lineTo(l4.x, l4.y);
shape.lineTo(l5.x, l5.y);
shape.lineTo(l6.x, l6.y);
shape.lineTo(l7.x, l7.y);
shape.closePath();
const shapeGeo = new ShapeGeometry(shape);
this.lineShape.geometry.dispose();
this.lineShape.geometry = shapeGeo;
}
updateDragShape(current: number) {
this.dragHandleArr[current + 1].setPosition(this.ptsArr[current + 1]);
this.textMesh?.position.copy(this.ptsArr[1]);
}
setMeshVisible(value: boolean) {
if (value === false) {
this.dragHandleArr.forEach((item) => {
item.setVisible(value);
});
}
this.lineShape.visible = value;
if (this.textMesh) this.textMesh.visible = value;
}
dispose() {
super.dispose();
this.scene.remove(this.textMesh);
}
intersectObjects(_rayCaster: Raycaster) {
let children: any = [];
children.push(this.lineShape);
if (this.textMesh) children.push(this.textMesh);
const intersects = _rayCaster.intersectObjects(children, true);
if (intersects.length > 0) {
return true;
}
return false;
}
changeMeshMaterial(material: Material) {
if (this.lineShape.material === material) {
return;
}
this.lineShape.material = material;
if (this.textMesh) this.textMesh.material = material;
}
getData() {
let pointArr: any = [];
this.ptsArr.forEach((item) => {
const arr = item.toArray();
pointArr.push(arr);
});
let obj = {
ptsArr: pointArr,
lineSize: this.lineSize,
textSize: this.textSize,
color: this.color,
text: this.text,
};
return obj;
}
}
export { OpAnnotationDwgLead };
文字
// OpAnnotationDwgText.ts
import {
Vector3,
Font,
Scene,
Mesh,
TextGeometry,
Raycaster,
Material,
} from "../../WebViewer/THREE/Three";
import { AnnotationMesh, OpAnnotationDwg } from "./OpAnnotationDwg";
class OpAnnotationDwgText extends OpAnnotationDwg {
constructor(app: any) {
super(app);
}
tempAnnotationPointerUp() {
super.stopAnnotation();
}
createText(text: string) {
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const point = new Vector3();
const mesh = new AnnotationTextMesh({
start: point,
end: point,
font,
scene,
lineSize: this.lineSize,
textSize: this.textSize,
color: this.color,
text: text,
});
return mesh;
}
createAnnotationFromData(data: {
ptsArr: Array<number[]>;
lineSize: number;
textSize: number;
color: string;
text: string;
}) {
let pointArr: Vector3[] = [];
data.ptsArr.forEach((item: number[]) => {
const v3 = new Vector3().fromArray(item);
pointArr.push(v3);
});
const scene = this.app.bimViewer.getIncrementalScene();
const font = this.app.bimViewer.getRenderContext().getWebGLFont();
const mesh = new AnnotationTextMesh({
start: pointArr[0],
end: pointArr[0],
font,
scene,
lineSize: data.lineSize,
textSize: data.textSize,
color: data.color,
text: data.text,
});
mesh.updateDragShape(0);
mesh.setDragHandleVisible(false);
return mesh;
}
}
class AnnotationTextMesh extends AnnotationMesh {
textMesh!: Mesh;
text: string;
constructor(param: {
start: Vector3;
end: Vector3;
font: Font;
scene: Scene;
lineSize: number;
textSize: number;
color: string;
text: string;
}) {
super(param);
this.ptsArr = [param.start.clone()];
this.text = "";
this._createLineTextShape(param.text);
}
private _createLineTextShape(text: string) {
this.text = text;
this.ptsArr.forEach((point) => {
const drag = this.createDragHandle(point);
this.dragHandleArr.push(drag);
drag.install(this.scene);
});
const textGeo = new TextGeometry(text, {
font: this.font,
size: this.textSize,
height: 0.001,
curveSegments: 10,
});
this.textMesh = new Mesh(textGeo, this.material);
this.scene.add(this.textMesh);
}
updateLineTextShape() {}
updateDragShape(current: number) {
this.dragHandleArr[0].setPosition(this.ptsArr[0]);
this.textMesh.position.copy(this.ptsArr[0]);
}
setMeshVisible(value: boolean) {
if (value === false) {
this.dragHandleArr.forEach((item) => {
item.setVisible(value);
});
}
this.textMesh.visible = value;
}
dispose() {
super.dispose();
this.scene.remove(this.textMesh);
}
intersectObjects(_rayCaster: Raycaster) {
let children: any = [];
children.push(this.textMesh);
const intersects = _rayCaster.intersectObjects(children, true);
if (intersects.length > 0) {
return true;
}
return false;
}
changeMeshMaterial(material: Material) {
if (this.textMesh.material === material) {
return;
}
this.textMesh.material = material;
}
getData() {
let pointArr: any = [];
this.ptsArr.forEach((item) => {
const arr = item.toArray();
pointArr.push(arr);
});
let obj = {
ptsArr: pointArr,
lineSize: this.lineSize,
textSize: this.textSize,
color: this.color,
text: this.text,
};
return obj;
}
}
export { OpAnnotationDwgText };
接口调用
现在对象都已经封装好了,可以进行业务调用了。封装一个标注管理器,对内进行统一的业务流程处理,外面统一接口调用
// AnnotationDwgManager.ts
import { OpAnnotationDwgCloud } from "./OpAnnotationDwgCloud";
import { OpAnnotationDwgRect } from "./OpAnnotationDwgRect";
import { OpAnnotationDwgCircle } from "./OpAnnotationDwgCircle";
import { OpAnnotationDwgLead } from "./OpAnnotationDwgLead";
import { OpAnnotationDwgText } from "./OpAnnotationDwgText";
/**
* 标注管理器,设置不同的标注模式
*/
class AnnotationDwgManager {
app: any;
opAnnotationDwgCloud: OpAnnotationDwgCloud;
opAnnotationDwgRect: OpAnnotationDwgRect;
opAnnotationDwgCircle: OpAnnotationDwgCircle;
opAnnotationDwgLead: OpAnnotationDwgLead;
opAnnotationDwgText: OpAnnotationDwgText;
annotationLineSize: number;
annotationTextSize: number;
annotationColor: string;
constructor(app: any) {
this.app = app;
this.opAnnotationDwgCloud = new OpAnnotationDwgCloud(this.app);
this.opAnnotationDwgRect = new OpAnnotationDwgRect(this.app);
this.opAnnotationDwgCircle = new OpAnnotationDwgCircle(this.app);
this.opAnnotationDwgLead = new OpAnnotationDwgLead(this.app);
this.opAnnotationDwgText = new OpAnnotationDwgText(this.app);
this.annotationLineSize = 2;
this.annotationTextSize = 500;
this.annotationColor = "#ff0000";
}
/**
* 销毁标注管理器
*/
dispose() {
this.opAnnotationDwgCloud.dispose();
this.opAnnotationDwgRect.dispose();
this.opAnnotationDwgCircle.dispose();
this.opAnnotationDwgLead.dispose();
this.opAnnotationDwgText.dispose();
// @ts-ignore
this.opAnnotationDwgCloud = null;
// @ts-ignore
this.opAnnotationDwgRect = null;
// @ts-ignore
this.opAnnotationDwgCircle = null;
// @ts-ignore
this.opAnnotationDwgLead = null;
// @ts-ignore
this.opAnnotationDwgText = null;
}
/**
* 设置标注模式
* @param type
*/
setAnnotationType(type: AnnotationDwgType) {
this.opAnnotationDwgCloud.disposeEvent();
this.opAnnotationDwgRect.disposeEvent();
this.opAnnotationDwgCircle.disposeEvent();
this.opAnnotationDwgLead.disposeEvent();
this.opAnnotationDwgText.disposeEvent();
switch (type) {
case AnnotationDwgType.CLOUD:
this.opAnnotationDwgCloud.initEvent();
break;
case AnnotationDwgType.RECT:
this.opAnnotationDwgRect.initEvent();
break;
case AnnotationDwgType.CIRCLE:
this.opAnnotationDwgCircle.initEvent();
break;
case AnnotationDwgType.LEAD:
this.opAnnotationDwgLead.initEvent();
break;
case AnnotationDwgType.TEXT:
this.opAnnotationDwgText.initEvent();
break;
}
}
/**
* 绑定引线文字标注引线绘制完成后的回调-执行弹窗输入文字
* @param callback
*/
bindLeadFinishCallback(callback: Function) {
this.opAnnotationDwgLead.bindFinishCallback(callback);
}
/**
* 引线标注的绘制文字接口
* @param text
*/
createLeadText(text: string) {
const pixelScale2d = this.app.bimViewer.mainCamera.getPixelScale2d();
//@ts-ignore
this.opAnnotationDwgLead.selectedAnnotation?.createLeadText(
text,
pixelScale2d
);
}
/**
* 文字标注的绘制文字接口
* @param text
*/
createText(text: string) {
const mesh = this.opAnnotationDwgText.createText(text);
this.opAnnotationDwgText.tempAnnotation = mesh;
this.opAnnotationDwgText.annotationMeshArr.push(mesh);
this.opAnnotationDwgText.setSelectAnnotation(null);
}
/**
* 删除选中标注结果
*/
deleteSelectAnnotation() {
this.opAnnotationDwgCloud.deleteSelectAnnotation();
this.opAnnotationDwgRect.deleteSelectAnnotation();
this.opAnnotationDwgCircle.deleteSelectAnnotation();
this.opAnnotationDwgLead.deleteSelectAnnotation();
this.opAnnotationDwgText.deleteSelectAnnotation();
}
/**
* 删除全部标注结果
*/
deleteAllAnnotation() {
this.opAnnotationDwgCloud.deleteAllAnnotation();
this.opAnnotationDwgRect.deleteAllAnnotation();
this.opAnnotationDwgCircle.deleteAllAnnotation();
this.opAnnotationDwgLead.deleteAllAnnotation();
this.opAnnotationDwgText.deleteAllAnnotation();
}
/**
* 设置所有标注结果显隐
* @param value
*/
setAnnotationMeshArrVisible(value: boolean) {
this.opAnnotationDwgCloud.setAnnotationMeshArrVisible(value);
this.opAnnotationDwgRect.setAnnotationMeshArrVisible(value);
this.opAnnotationDwgCircle.setAnnotationMeshArrVisible(value);
this.opAnnotationDwgLead.setAnnotationMeshArrVisible(value);
this.opAnnotationDwgText.setAnnotationMeshArrVisible(value);
}
/**
* 设置下一个标注线框
* @param value
*/
setNextAnnotationLineSize(val: number) {
if (val < 1) val = 1;
if (val > 10) val = 10;
this.annotationLineSize = val;
this.opAnnotationDwgCloud.setNextLineSize(val);
this.opAnnotationDwgRect.setNextLineSize(val);
this.opAnnotationDwgCircle.setNextLineSize(val);
this.opAnnotationDwgLead.setNextLineSize(val);
this.opAnnotationDwgText.setNextLineSize(val);
}
/**
* 设置下一个文字大小
* @param value
*/
setNextAnnotationTextSize(val: number) {
this.annotationTextSize = val;
this.opAnnotationDwgCloud.setNextTextSize(val);
this.opAnnotationDwgRect.setNextTextSize(val);
this.opAnnotationDwgCircle.setNextTextSize(val);
this.opAnnotationDwgLead.setNextTextSize(val);
this.opAnnotationDwgText.setNextTextSize(val);
}
/**
* 设置下一个标注颜色
* @param value
*/
setNextAnnotationColor(color: string) {
this.annotationColor = color;
this.opAnnotationDwgCloud.setNextColor(color);
this.opAnnotationDwgRect.setNextColor(color);
this.opAnnotationDwgCircle.setNextColor(color);
this.opAnnotationDwgLead.setNextColor(color);
this.opAnnotationDwgText.setNextColor(color);
}
/**
* 获取所有标注数据
*/
getAnnotationAllData() {
const cloud = this.opAnnotationDwgCloud.getAnnotationData();
const rect = this.opAnnotationDwgRect.getAnnotationData();
const circle = this.opAnnotationDwgCircle.getAnnotationData();
const lead = this.opAnnotationDwgLead.getAnnotationData();
const text = this.opAnnotationDwgText.getAnnotationData();
const data = { cloud, rect, circle, lead, text };
return JSON.stringify(data);
}
/**
* 设置所有标注
* @param json
*/
setAnnotationAllData(json: string) {
this.deleteAllAnnotation();
const data = JSON.parse(json);
this.opAnnotationDwgCloud.setAnnotationData(data.cloud);
this.opAnnotationDwgRect.setAnnotationData(data.rect);
this.opAnnotationDwgCircle.setAnnotationData(data.circle);
this.opAnnotationDwgLead.setAnnotationData(data.lead);
this.opAnnotationDwgText.setAnnotationData(data.text);
}
}
enum AnnotationDwgType {
/**
* 云线
*/
CLOUD = 1,
/**
* 矩形
*/
RECT = 2,
/**
* 圆
*/
CIRCLE = 3,
/**
* 引线
*/
LEAD = 4,
/**
* 文字
*/
TEXT = 5,
}
export { AnnotationDwgManager, AnnotationDwgType };
最后只需要调用这个 Manager 的接口,就能轻松实现。业务调用方代码量极少。