922 lines
29 KiB
TypeScript
922 lines
29 KiB
TypeScript
import * as THREE from "three";
|
||
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
|
||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||
import { TTFLoader } from "three/examples/jsm/loaders/TTFLoader.js";
|
||
// import { SSAARenderPass } from "three/examples/jsm/postprocessing/SSAARenderPass.js";
|
||
// import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";
|
||
// import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
|
||
// import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
|
||
// import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';
|
||
import {
|
||
addTriangleFacingCamera,
|
||
getBSplinePolyline, getBulgeCurvePoints,
|
||
mtextContentAndFormattingToTextAndStyle
|
||
} from "./drawUtils";
|
||
// @ts-ignore
|
||
import { parseDxfMTextContent } from "@dxfom/mtext";
|
||
import { Font } from "three/examples/jsm/loaders/FontLoader.js";
|
||
import { PickHelper } from "./pickHelper";
|
||
import { DrawRect } from "./drawRect";
|
||
import log from '@/utils/log/Logger';
|
||
|
||
let signal, middleObject;
|
||
let scene, helpScene, camera, renderer, inputElement, font, data;
|
||
|
||
// 渲染辉光图层
|
||
// let composer, finalComposer;
|
||
const BLOOM_SCENE = 10;
|
||
const bloomLayer = new THREE.Layers();
|
||
bloomLayer.set(BLOOM_SCENE);
|
||
// const DarkMaterial = new THREE.MeshBasicMaterial({ color: 'black' });
|
||
// const materials = {};
|
||
|
||
let modules: any = {};
|
||
|
||
// TODO 当前解析模式会存在 data.blocks 中还有实体但未匹配解析的情况;
|
||
// 记录已经解析的 data.blocks,data.entities遍历完成后处理
|
||
let parsedBlocks: string[] = [];
|
||
// 渲染配置项 && 对比度颜色实体集合
|
||
let options = {
|
||
bgColor: 0x000000,
|
||
contrastColor: 0xffffff,
|
||
fontUrl:''
|
||
}, contrastEntity: any = [];
|
||
|
||
// canvas默认宽高
|
||
export const state = {
|
||
width: 500,
|
||
height: 500,
|
||
};
|
||
|
||
/**
|
||
* 离屏canvas和普通canvas共用的绘制图纸方法
|
||
* 优先级:offScreenCanvas > canvas
|
||
*/
|
||
export function main(d) {
|
||
const { canvas, onComplete } = d;
|
||
signal = d.signal;
|
||
middleObject = d.middleObject;
|
||
state.width = canvas.width;
|
||
state.height = canvas.height;
|
||
inputElement = d.inputElement;
|
||
data = d.data;
|
||
// cad配置项
|
||
options = Object.assign(options, d.options);
|
||
options.bgColor = Number(options.bgColor);
|
||
options.contrastColor = options.bgColor === 0x000000 ? 0xffffff : 0x000000;
|
||
|
||
createLineTypeShaders();
|
||
|
||
renderer = new THREE.WebGLRenderer({
|
||
canvas,
|
||
powerPreference: "high-performance",
|
||
depth: false,
|
||
antialias: true,
|
||
});
|
||
// 第三个参数false代表不更新canvas dom style
|
||
renderer.setSize(canvas.width, canvas.height, false);
|
||
renderer.setClearColor(options.bgColor, 1);
|
||
renderer.toneMapping = THREE.ReinhardToneMapping;
|
||
// renderer.autoClear = false;
|
||
|
||
const loader = new TTFLoader();
|
||
loader.loadAsync(options.fontUrl).then(function (response) {
|
||
font = new Font(response);
|
||
|
||
init();
|
||
|
||
// OrbitControls mousemove事件中未调用chang事件,所以需要一直渲染
|
||
renderLoop();
|
||
|
||
// 添加灯光
|
||
scene.add(new THREE.AmbientLight(0xffffff, 1));
|
||
|
||
// 后期通道
|
||
// initComposer();
|
||
|
||
const controls = new OrbitControls(camera, inputElement);
|
||
controls.target.x = camera.position.x;
|
||
controls.target.y = camera.position.y;
|
||
controls.target.z = 0;
|
||
controls.mouseButtons = { LEFT: THREE.MOUSE.PAN, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.ROTATE };
|
||
controls.enableRotate = false;
|
||
controls.update();
|
||
|
||
// 注册模块
|
||
modules.drawRect = new DrawRect(inputElement, helpScene, camera, signal, middleObject);
|
||
modules.drawRect.setControls(controls);
|
||
|
||
const pickPosition = new THREE.Vector2(0, 0);
|
||
modules.pickHelper = new PickHelper(scene, camera, BLOOM_SCENE);
|
||
clearPickPosition();
|
||
|
||
function getCanvasRelativePosition(event) {
|
||
const rect = inputElement.getBoundingClientRect();
|
||
return {
|
||
x: event.clientX - rect.left,
|
||
y: event.clientY - rect.top,
|
||
};
|
||
}
|
||
|
||
function setPickPosition(event) {
|
||
const pos = getCanvasRelativePosition(event);
|
||
pickPosition.x = (pos.x / state.width) * 2 - 1;
|
||
pickPosition.y = (pos.y / state.height) * - 2 + 1;
|
||
|
||
modules.pickHelper.pick(pickPosition);
|
||
}
|
||
|
||
function clearPickPosition() {
|
||
//不像鼠标总是有一个位置,如果用户停止触摸屏幕,我们想要停止。现在我们只取一个不太可能取到的值
|
||
pickPosition.x = - 100000;
|
||
pickPosition.y = - 100000;
|
||
}
|
||
|
||
inputElement.addEventListener('mousemove', setPickPosition);
|
||
inputElement.addEventListener('mouseout', clearPickPosition);
|
||
inputElement.addEventListener('mouseleave', clearPickPosition);
|
||
|
||
inputElement.addEventListener('touchstart', (event) => {
|
||
// 防止窗口滚动
|
||
event.preventDefault();
|
||
setPickPosition(event.touches[0]);
|
||
}, { passive: false });
|
||
inputElement.addEventListener('touchmove', (event) => {
|
||
setPickPosition(event.touches[0]);
|
||
});
|
||
inputElement.addEventListener('touchend', clearPickPosition);
|
||
|
||
// 加载完毕
|
||
onComplete && onComplete();
|
||
|
||
font = undefined;
|
||
data = undefined;
|
||
|
||
resize({ width: inputElement.width, height: inputElement.height });
|
||
});
|
||
}
|
||
|
||
function createLineTypeShaders() {
|
||
let ltype, type;
|
||
if (!data.tables || !data.tables.lineType) return;
|
||
const ltypes = data.tables.lineType.lineTypes;
|
||
|
||
for (type in ltypes) {
|
||
ltype = ltypes[type];
|
||
if (!ltype.pattern) continue;
|
||
ltype.material = createDashedLineShader(ltype.pattern);
|
||
}
|
||
}
|
||
|
||
function createDashedLineShader(pattern) {
|
||
let dashedLineShader: any = {},
|
||
totalLength = 0.0;
|
||
|
||
for (let i = 0; i < pattern.length; i++) {
|
||
totalLength += Math.abs(pattern[i]);
|
||
}
|
||
|
||
dashedLineShader.uniforms = THREE.UniformsUtils.merge([
|
||
THREE.UniformsLib['common'],
|
||
THREE.UniformsLib['fog'],
|
||
|
||
{
|
||
// @ts-ignore
|
||
'pattern': { type: 'fv1', value: pattern },
|
||
// @ts-ignore
|
||
'patternLength': { type: 'f', value: totalLength }
|
||
}
|
||
]);
|
||
|
||
dashedLineShader.vertexShader = [
|
||
'attribute float lineDistance;',
|
||
|
||
'varying float vLineDistance;',
|
||
|
||
THREE.ShaderChunk['color_pars_vertex'],
|
||
|
||
'void main() {',
|
||
|
||
THREE.ShaderChunk['color_vertex'],
|
||
|
||
'vLineDistance = lineDistance;',
|
||
|
||
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||
|
||
'}'
|
||
].join('\n');
|
||
|
||
dashedLineShader.fragmentShader = [
|
||
'uniform vec3 diffuse;',
|
||
'uniform float opacity;',
|
||
|
||
'uniform float pattern[' + pattern.length + '];',
|
||
'uniform float patternLength;',
|
||
|
||
'varying float vLineDistance;',
|
||
|
||
THREE.ShaderChunk['color_pars_fragment'],
|
||
THREE.ShaderChunk['fog_pars_fragment'],
|
||
|
||
'void main() {',
|
||
|
||
'float pos = mod(vLineDistance, patternLength);',
|
||
|
||
'for ( int i = 0; i < ' + pattern.length + '; i++ ) {',
|
||
'pos = pos - abs(pattern[i]);',
|
||
'if( pos < 0.0 ) {',
|
||
'if( pattern[i] > 0.0 ) {',
|
||
'gl_FragColor = vec4(1.0, 0.0, 0.0, opacity );',
|
||
'break;',
|
||
'}',
|
||
'discard;',
|
||
'}',
|
||
|
||
'}',
|
||
|
||
THREE.ShaderChunk['color_fragment'],
|
||
THREE.ShaderChunk['fog_fragment'],
|
||
|
||
'}'
|
||
].join('\n');
|
||
|
||
return dashedLineShader;
|
||
}
|
||
|
||
function init() {
|
||
scene = new THREE.Scene();
|
||
scene.background = new THREE.Color(options.bgColor);
|
||
helpScene = new THREE.Scene();
|
||
|
||
const layersGroupMap: Map<string, THREE.Group> = new Map();
|
||
// 使用layers生成group
|
||
for (let layerName in data.tables.layer.layers) {
|
||
const layer = data.tables.layer.layers[layerName];
|
||
const group = new THREE.Group();
|
||
group.name = layerName;
|
||
group.visible = layer.visible;
|
||
|
||
layersGroupMap.set(layerName, group);
|
||
scene.add(group);
|
||
}
|
||
|
||
// 通过dxf数据创建场景
|
||
let entity, obj;
|
||
// 生成所有实体
|
||
for (let i = 0; i < data.entities?.length; i++) {
|
||
entity = data.entities[i];
|
||
|
||
obj = drawEntity(entity);
|
||
|
||
if (obj) {
|
||
if (layersGroupMap.has(entity.layer)) {
|
||
const parent = layersGroupMap.get(entity.layer) || scene;
|
||
parent.add(obj);
|
||
obj.name = `${entity.type}-${entity.handle || 'noHandle'}-${parent.children.length}`
|
||
} else {
|
||
scene.add(obj);
|
||
obj.name = `${entity.type}-${entity.handle || 'noHandle'}-${scene.children.length}`
|
||
}
|
||
}
|
||
obj = null;
|
||
}
|
||
|
||
// 2023-9-6:遍历所有剩下的未遍历的块,找出其中 INSERT/DIMENSION 类型的实体,进行解析.(非此类型就算解析位置也不正确)
|
||
// TODO 待删除
|
||
for (let blockName in data.blocks) {
|
||
const block = data.blocks[blockName];
|
||
|
||
if (parsedBlocks.includes(blockName) || !block.position || !block.entities) continue;
|
||
|
||
for (let j = 0; j < block.entities.length; j++) {
|
||
entity = block.entities[j];
|
||
if (entity.type == "INSERT" || entity.type == "DIMENSION") {
|
||
// TODO 容易栈溢出
|
||
// obj = this.drawEntity(entity);
|
||
//
|
||
// if (obj) {
|
||
// const bbox = new THREE.Box3().setFromObject(obj);
|
||
// if (isFinite(bbox.min.x) && (dims.min.x > bbox.min.x)) dims.min.x = bbox.min.x;
|
||
// if (isFinite(bbox.min.y) && (dims.min.y > bbox.min.y)) dims.min.y = bbox.min.y;
|
||
// if (isFinite(bbox.min.z) && (dims.min.z > bbox.min.z)) dims.min.z = bbox.min.z;
|
||
// if (isFinite(bbox.max.x) && (dims.max.x < bbox.max.x)) dims.max.x = bbox.max.x;
|
||
// if (isFinite(bbox.max.y) && (dims.max.y < bbox.max.y)) dims.max.y = bbox.max.y;
|
||
// if (isFinite(bbox.max.z) && (dims.max.z < bbox.max.z)) dims.max.z = bbox.max.z;
|
||
// this.scene.add(obj);
|
||
// }
|
||
// obj = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
const aspectRatio = state.width / state.height;
|
||
const dims = new THREE.Box3().setFromObject(scene);
|
||
const upperRightCorner = { x: dims.max.x, y: dims.max.y };
|
||
const lowerLeftCorner = { x: dims.min.x, y: dims.min.y };
|
||
//const lowerLeftCorner = data.header.$EXTMIN; // X、Y 和 Z 图形范围左下角(在 WCS 中)
|
||
//const upperRightCorner = data.header.$EXTMAX; // X、Y 和 Z 图形范围右上角(在 WCS 中)
|
||
|
||
// 找出当前的视口范围
|
||
let vp_width = upperRightCorner.x - lowerLeftCorner.x;
|
||
let vp_height = upperRightCorner.y - lowerLeftCorner.y;
|
||
let center = {
|
||
x: vp_width / 2 + lowerLeftCorner.x,
|
||
y: vp_height / 2 + lowerLeftCorner.y
|
||
};
|
||
//let center = data.tables.viewPort.viewPorts[0].ucsOrigin;
|
||
|
||
// 将所有对象放入当前的查看器中
|
||
const extentsAspectRatio = Math.abs(vp_width / vp_height);
|
||
if (aspectRatio > extentsAspectRatio) {
|
||
vp_width = vp_height * aspectRatio;
|
||
} else {
|
||
vp_height = vp_width / aspectRatio;
|
||
}
|
||
|
||
const viewPort = {
|
||
bottom: -vp_height / 2,
|
||
left: -vp_width / 2,
|
||
top: vp_height / 2,
|
||
right: vp_width / 2,
|
||
center: {
|
||
x: center.x,
|
||
y: center.y
|
||
}
|
||
};
|
||
|
||
camera = new THREE.OrthographicCamera(viewPort.left, viewPort.right, viewPort.top, viewPort.bottom, 1, 19);
|
||
camera.position.z = 1;
|
||
camera.position.x = viewPort.center.x;
|
||
camera.position.y = viewPort.center.y;
|
||
camera.userData.viewPort = viewPort;
|
||
|
||
// 销毁中间变量
|
||
layersGroupMap.clear();
|
||
}
|
||
|
||
// function initComposer() {
|
||
// const pixelRatio = renderer.getPixelRatio();
|
||
//
|
||
// composer = new EffectComposer(renderer);
|
||
// composer.renderToScreen = false;
|
||
// composer.setPixelRatio(pixelRatio)
|
||
//
|
||
// let ssaaRenderPass = new SSAARenderPass(scene, camera);
|
||
// ssaaRenderPass.unbiased = false;
|
||
// ssaaRenderPass.sampleLevel = 2;
|
||
// composer.addPass(ssaaRenderPass);
|
||
//
|
||
// const bloomPass = new UnrealBloomPass(
|
||
// //参数一:泛光覆盖场景大小,二维向量类型
|
||
// new THREE.Vector2(inputElement.width, inputElement.height),
|
||
// //参数二:bloomStrength 泛光强度,值越大明亮的区域越亮,较暗区域变亮的范围越广
|
||
// 1.5,
|
||
// //参数三:bloomRadius 泛光散发半径
|
||
// 0,
|
||
// //参数四:bloomThreshold 泛光的光照强度阈值,如果照在物体上的光照强度大于该值就会产生泛光
|
||
// 0
|
||
// );
|
||
// composer.addPass(bloomPass);
|
||
//
|
||
// const mixPass = new ShaderPass(
|
||
// new THREE.ShaderMaterial({
|
||
// uniforms: {
|
||
// baseTexture: { value: null },
|
||
// bloomTexture: { value: composer.renderTarget2.texture }
|
||
// },
|
||
// vertexShader: `
|
||
// varying vec2 vUv;
|
||
// void main() {
|
||
// vUv = uv;
|
||
// gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
||
// }
|
||
// `,
|
||
// fragmentShader: `
|
||
// uniform sampler2D baseTexture;
|
||
// uniform sampler2D bloomTexture;
|
||
// varying vec2 vUv;
|
||
// void main() {
|
||
// gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
|
||
// }`,
|
||
// defines: {}
|
||
// }), 'baseTexture'
|
||
// );
|
||
// mixPass.needsSwap = true;
|
||
//
|
||
// const outputPass = new OutputPass();
|
||
//
|
||
// finalComposer = new EffectComposer(renderer);
|
||
// finalComposer.addPass(ssaaRenderPass);
|
||
// finalComposer.addPass(mixPass);
|
||
// finalComposer.addPass(outputPass);
|
||
// }
|
||
|
||
// 图元选中
|
||
export function select({ modelOrName }) {
|
||
let model = modelOrName;
|
||
if (typeof modelOrName === 'string') {
|
||
model = scene.getObjectByProperty('name', modelOrName);
|
||
}
|
||
|
||
if (model) {
|
||
modules.pickHelper.select(model);
|
||
}
|
||
}
|
||
|
||
/** ------------------------------------------实体解析部分-------------------------------------------------------- **/
|
||
function getColor(entity) {
|
||
let color = options.contrastColor;//默认高对比度颜色
|
||
if (entity.color !== undefined) color = entity.color;
|
||
else if (data.tables && data.tables.layer && data.tables.layer.layers[entity.layer])
|
||
color = data.tables.layer.layers[entity.layer].color;
|
||
|
||
// 因为 scene.background = new THREE.Color(options.bgColor),所以黑色显示时改为白色
|
||
if (color == null || color === 0 || color === options.bgColor) {
|
||
color = options.contrastColor;
|
||
}
|
||
return color;
|
||
}
|
||
|
||
function drawEntity(entity) {
|
||
// 2023-9-6: 仅解析模型空间的实体
|
||
if (entity.inPaperSpace) return null;
|
||
|
||
let mesh;
|
||
switch (entity.type) {
|
||
case 'CIRCLE':
|
||
case 'ARC':
|
||
mesh = drawArc(entity);
|
||
break;
|
||
case 'LWPOLYLINE':
|
||
case 'LINE':
|
||
case 'POLYLINE':
|
||
mesh = drawLine(entity);
|
||
break;
|
||
case 'TEXT':
|
||
mesh = drawText(entity);
|
||
break;
|
||
case 'ATTDEF':
|
||
mesh = drawAttdef(entity);
|
||
break;
|
||
case 'ATTRIB':
|
||
mesh = drawAttrib(entity);
|
||
break;
|
||
case 'SOLID':
|
||
mesh = drawSolid(entity);
|
||
break;
|
||
case 'POINT':
|
||
mesh = drawPoint(entity);
|
||
break;
|
||
case 'INSERT':
|
||
mesh = drawBlock(entity);
|
||
break;
|
||
case 'SPLINE':
|
||
mesh = drawSpline(entity);
|
||
break;
|
||
case 'MTEXT':
|
||
// console.log("实体类型 MTEXT:", entity);
|
||
mesh = drawMtext(entity);
|
||
break;
|
||
case 'ELLIPSE':
|
||
mesh = drawEllipse(entity);
|
||
break;
|
||
case "HATCH":
|
||
mesh = drawHatch(entity);
|
||
break;
|
||
case 'DIMENSION':
|
||
const dimTypeEnum = entity.dimensionType & 7;
|
||
if (dimTypeEnum === 0) {
|
||
mesh = drawDimension(entity);
|
||
} else {
|
||
//console.log("不支持的维度类型: " + dimTypeEnum);
|
||
}
|
||
break;
|
||
default:
|
||
log.warn("不支持的实体类型: " + entity.type);
|
||
break;
|
||
}
|
||
|
||
if (mesh?.material && mesh.material?.color.getHex() === options.contrastColor) {
|
||
contrastEntity.push(mesh)
|
||
}
|
||
|
||
return mesh;
|
||
}
|
||
|
||
function drawArc(entity) {
|
||
let startAngle: number, endAngle: number;
|
||
if (entity.type === 'CIRCLE') {
|
||
startAngle = entity.startAngle || 0;
|
||
endAngle = startAngle + 2 * Math.PI;
|
||
} else {
|
||
startAngle = entity.startAngle;
|
||
endAngle = entity.endAngle;
|
||
}
|
||
|
||
//@ts-ignore
|
||
const curve = new THREE.ArcCurve(
|
||
0, 0,
|
||
entity.radius,
|
||
startAngle,
|
||
endAngle);
|
||
|
||
const points = curve.getPoints(32);
|
||
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
||
|
||
const material = new THREE.LineBasicMaterial({ color: getColor(entity) });
|
||
|
||
const arc = new THREE.Line(geometry, material);
|
||
arc.position.x = entity.center.x;
|
||
arc.position.y = entity.center.y;
|
||
arc.position.z = entity.center.z;
|
||
|
||
return arc;
|
||
}
|
||
|
||
function drawLine(entity) {
|
||
let points: THREE.Vector3[] = [];
|
||
let color = getColor(entity);
|
||
let material, lineType, vertex, startPoint, endPoint,
|
||
bulge, i, line;
|
||
|
||
if (!entity.vertices) return; //console.log('缺少顶点的实体.');
|
||
|
||
// 创建几何
|
||
for (i = 0; i < entity.vertices.length; i++) {
|
||
if (entity.vertices[i].bulge) {
|
||
bulge = entity.vertices[i].bulge;
|
||
startPoint = entity.vertices[i];
|
||
endPoint = i + 1 < entity.vertices.length ? entity.vertices[i + 1] : points[0];
|
||
|
||
let bulgePoints = getBulgeCurvePoints(startPoint, endPoint, bulge);
|
||
|
||
points.push.apply(points, bulgePoints);
|
||
} else {
|
||
vertex = entity.vertices[i];
|
||
points.push(new THREE.Vector3(vertex.x, vertex.y, 0));
|
||
}
|
||
|
||
}
|
||
if (entity.shape) points.push(points[0]);
|
||
|
||
|
||
// 设置材质
|
||
if (entity.lineType) {
|
||
lineType = data.tables.lineType.lineTypes[entity.lineType];
|
||
}
|
||
|
||
if (lineType && lineType.pattern && lineType.pattern.length !== 0) {
|
||
material = new THREE.LineDashedMaterial({ color: color, gapSize: 4, dashSize: 4 });
|
||
} else {
|
||
material = new THREE.LineBasicMaterial({ linewidth: 1, color: color });
|
||
}
|
||
|
||
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
||
|
||
line = new THREE.Line(geometry, material);
|
||
return line;
|
||
}
|
||
|
||
function drawText(entity) {
|
||
let geometry, material, text;
|
||
|
||
if (!font)
|
||
return log.warn('文本不支持没有Three.js字体加载的THREE.FontLoader!');
|
||
|
||
if (entity.text == null || entity.text == "") return null;
|
||
|
||
if (entity.textHeight == null || entity.textHeight == 0) return null;
|
||
|
||
// 当前使用的字体略宽,需要缩小一点
|
||
// xScale: 相对 X 比例因子 — 宽度
|
||
let fontSize = entity.xScale ? entity.xScale * entity.textHeight * 0.73 : entity.textHeight * 0.5;
|
||
geometry = new TextGeometry(entity.text, { font: font, depth: 0, size: fontSize || 100 });
|
||
|
||
if (entity.rotation) {
|
||
const zRotation = entity.rotation * Math.PI / 180;
|
||
geometry.rotateZ(zRotation);
|
||
}
|
||
|
||
material = new THREE.MeshBasicMaterial({ color: getColor(entity) });
|
||
|
||
text = new THREE.Mesh(geometry, material);
|
||
text.position.x = entity.startPoint.x;
|
||
text.position.y = entity.startPoint.y;
|
||
text.position.z = entity.startPoint.z;
|
||
|
||
return text;
|
||
}
|
||
|
||
function drawMtext(entity) {
|
||
const color = getColor(entity);
|
||
|
||
const textAndControlChars = parseDxfMTextContent(entity.text);
|
||
|
||
//Note: 目前只支持应用于所有mtext文本的单一格式
|
||
const content = mtextContentAndFormattingToTextAndStyle(textAndControlChars, entity, color);
|
||
|
||
/* 单行文本渲染模式 */
|
||
if (!font)
|
||
return log.warn('文本不支持没有Three.js字体加载的THREE.FontLoader!');
|
||
|
||
if (content.text == null || content.text == "") return null;
|
||
if (content.style.textHeight == null || content.style.textHeight == 0) return null;
|
||
|
||
const geometry = new TextGeometry(content.text, { font: font, depth: 0, size: content.style.textHeight || 100 });
|
||
if (entity.rotation) {
|
||
const zRotation = entity.rotation * Math.PI / 180;
|
||
geometry.rotateZ(zRotation);
|
||
}
|
||
if (entity.directionVector) {
|
||
const dv = entity.directionVector;
|
||
geometry.rotateZ(new THREE.Vector3(1, 0, 0).angleTo(new THREE.Vector3(dv.x, dv.y, dv.z)));
|
||
}
|
||
const material = new THREE.MeshBasicMaterial({ color });
|
||
const text = new THREE.Mesh(geometry, material);
|
||
text.position.x = entity.position.x;
|
||
text.position.y = entity.position.y;
|
||
text.position.z = entity.position.z;
|
||
|
||
return text;
|
||
|
||
|
||
/* TODO:多行文本渲染,拉近不显示 */
|
||
/*const txt = createTextForScene(content.text, content.style, entity, color,options.fontUrl);
|
||
if (!txt) return null;
|
||
|
||
console.log("MText txt:", txt);
|
||
|
||
// const group = new THREE.Object3D();
|
||
// group.add(txt);
|
||
return txt;*/
|
||
}
|
||
|
||
// @ts-ignore
|
||
function drawAttdef(entity) {
|
||
//console.log("drawAttdef:", entity);
|
||
}
|
||
|
||
// @ts-ignore
|
||
function drawAttrib(entity) {
|
||
//console.log("drawAttrib:", entity);
|
||
}
|
||
|
||
function drawSolid(entity) {
|
||
let material, verts,
|
||
geometry = new THREE.BufferGeometry();
|
||
|
||
const points = entity.points;
|
||
// verts = geometry.vertices;
|
||
verts = [];
|
||
addTriangleFacingCamera(verts, points[0], points[1], points[2]);
|
||
addTriangleFacingCamera(verts, points[1], points[2], points[3]);
|
||
|
||
material = new THREE.MeshBasicMaterial({ color: getColor(entity) });
|
||
geometry.setFromPoints(verts);
|
||
|
||
return new THREE.Mesh(geometry, material);
|
||
}
|
||
|
||
function drawPoint(entity) {
|
||
let geometry, material;
|
||
|
||
geometry = new THREE.BufferGeometry();
|
||
|
||
geometry.setAttribute('position', new THREE.Float32BufferAttribute([entity.position.x, entity.position.y, entity.position.z], 3));
|
||
|
||
const color = getColor(entity);
|
||
|
||
material = new THREE.PointsMaterial({ size: 0.1, color: new THREE.Color(color) });
|
||
return new THREE.Points(geometry, material);
|
||
// this.scene.add(point);
|
||
}
|
||
|
||
function drawBlock(entity) {
|
||
if (!data.blocks) return;
|
||
|
||
let block = data.blocks[entity.name];
|
||
|
||
if (!block) return null;
|
||
|
||
// 解析过的就存储起来,不再重复解析
|
||
parsedBlocks.push(entity.name);
|
||
|
||
if (!block.entities) return null;
|
||
|
||
const group = new THREE.Group();
|
||
|
||
if (entity.xScale) group.scale.x = entity.xScale;
|
||
if (entity.yScale) group.scale.y = entity.yScale;
|
||
|
||
if (entity.rotation) {
|
||
group.rotation.z = entity.rotation * Math.PI / 180;
|
||
}
|
||
|
||
if (entity.position) {
|
||
group.position.x = entity.position.x;
|
||
group.position.y = entity.position.y;
|
||
group.position.z = entity.position.z;
|
||
}
|
||
|
||
for (let i = 0; i < block.entities.length; i++) {
|
||
const childEntity = drawEntity(block.entities[i]);
|
||
if (childEntity) group.add(childEntity);
|
||
}
|
||
|
||
return group;
|
||
}
|
||
|
||
function drawSpline(entity) {
|
||
if (entity.controlPoints === undefined) return null;
|
||
const color = getColor(entity);
|
||
|
||
const points = getBSplinePolyline(entity.controlPoints, entity.degreeOfSplineCurve, entity.knotValues, 100, undefined);
|
||
|
||
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
||
const material = new THREE.LineBasicMaterial({ linewidth: 1, color: color });
|
||
return new THREE.Line(geometry, material);
|
||
}
|
||
|
||
function drawEllipse(entity) {
|
||
const color = getColor(entity);
|
||
|
||
const xrad = Math.sqrt(Math.pow(entity.majorAxisEndPoint.x, 2) + Math.pow(entity.majorAxisEndPoint.y, 2));
|
||
const yrad = xrad * entity.axisRatio;
|
||
const rotation = Math.atan2(entity.majorAxisEndPoint.y, entity.majorAxisEndPoint.x);
|
||
|
||
const curve = new THREE.EllipseCurve(
|
||
entity.center.x, entity.center.y,
|
||
xrad, yrad,
|
||
entity.startAngle, entity.endAngle,
|
||
false, // Always counterclockwise
|
||
rotation
|
||
);
|
||
|
||
const points = curve.getPoints(50);
|
||
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
||
const material = new THREE.LineBasicMaterial({ linewidth: 1, color: color });
|
||
|
||
// 创建要添加到场景中的最后一个对象
|
||
return new THREE.Line(geometry, material);
|
||
}
|
||
|
||
function drawHatch(entity) {
|
||
if (entity.isSolid) return
|
||
|
||
// TODO 未实现
|
||
}
|
||
|
||
function drawDimension(entity) {
|
||
if (!data.blocks) return;
|
||
|
||
// console.log("drawDimension", entity.block)
|
||
const block = data.blocks[entity.block];
|
||
|
||
// 解析过的就存储起来,不再重复解析
|
||
parsedBlocks.push(entity.block);
|
||
|
||
if (!block || !block.entities) return null;
|
||
|
||
const group = new THREE.Group();
|
||
// if(entity.anchorPoint) {
|
||
// group.position.x = entity.anchorPoint.x;
|
||
// group.position.y = entity.anchorPoint.y;
|
||
// group.position.z = entity.anchorPoint.z;
|
||
// }
|
||
|
||
for (let i = 0; i < block.entities.length; i++) {
|
||
const childEntity = drawEntity(block.entities[i]);
|
||
if (childEntity) group.add(childEntity);
|
||
}
|
||
|
||
return group;
|
||
}
|
||
|
||
/** ------------------------------------------实体解析结束-------------------------------------------------------- **/
|
||
|
||
// 设置图层可见性
|
||
export function setLayerVisible(data: { layerName: string, visible: boolean }) {
|
||
const group = scene.getObjectByName(data.layerName);
|
||
if (group) group.visible = data.visible;
|
||
}
|
||
|
||
// 调用modules里的方法
|
||
export function callModuleMethod(data: { moduleName: string, methodName: string }) {
|
||
const module = modules[data.moduleName];
|
||
if (module && module[data.methodName]) {
|
||
module[data.methodName](data);
|
||
}
|
||
}
|
||
|
||
// 相机复位
|
||
export function resetCamera() {
|
||
const viewPort = camera.userData.viewPort;
|
||
|
||
if (!viewPort) return;
|
||
|
||
camera.left = viewPort.left;
|
||
camera.right = viewPort.right;
|
||
camera.top = viewPort.top;
|
||
camera.bottom = viewPort.bottom;
|
||
|
||
camera.position.z = 1;
|
||
camera.position.x = viewPort.center.x;
|
||
camera.position.y = viewPort.center.y;
|
||
|
||
modules.drawRect.controls.target.x = camera.position.x;
|
||
modules.drawRect.controls.target.y = camera.position.y;
|
||
modules.drawRect.controls.target.z = 0;
|
||
modules.drawRect.controls.object.zoom = 1;
|
||
modules.drawRect.controls.update();
|
||
}
|
||
|
||
//let start;
|
||
export function resize({ width, height }) {
|
||
if (!camera || data) return;
|
||
|
||
const hscale = width / state.width;
|
||
const vscale = height / state.height;
|
||
|
||
state.width = width;
|
||
state.height = height;
|
||
|
||
camera.top = (vscale * camera.top);
|
||
camera.bottom = (vscale * camera.bottom);
|
||
camera.left = (hscale * camera.left);
|
||
camera.right = (hscale * camera.right);
|
||
|
||
camera.updateProjectionMatrix();
|
||
|
||
renderer.setSize(width, height, false);
|
||
// composer.setSize(width, height);
|
||
// finalComposer.setSize(width, height);
|
||
|
||
render();
|
||
}
|
||
|
||
//
|
||
// function darkenNonBloomed(obj) {
|
||
// if (obj.material && bloomLayer.test(obj.layers) === false) {
|
||
// materials[obj.uuid] = obj.material;
|
||
// obj.material = DarkMaterial;
|
||
// }
|
||
// }
|
||
//
|
||
// function restoreMaterial(obj) {
|
||
// if (materials[obj.uuid]) {
|
||
// obj.material = materials[obj.uuid];
|
||
// delete materials[obj.uuid];
|
||
// }
|
||
// }
|
||
|
||
export function render() {
|
||
renderer.autoClear = false;
|
||
|
||
// scene.traverse(darkenNonBloomed);
|
||
// composer.render();
|
||
// scene.traverse(restoreMaterial);
|
||
// finalComposer.render();
|
||
renderer.render(scene, camera);
|
||
renderer.render(helpScene, camera);
|
||
|
||
renderer.autoClear = true;
|
||
}
|
||
|
||
export function stopRender() {
|
||
renderer.setAnimationLoop(null)
|
||
}
|
||
|
||
export function renderLoop() {
|
||
renderer.setAnimationLoop(render)
|
||
}
|
||
|
||
export function helpRender() {
|
||
renderer.autoClear = false;
|
||
renderer.render(helpScene, camera);
|
||
}
|
||
|
||
export function dispose() {
|
||
data = undefined;
|
||
font = undefined;
|
||
|
||
renderer.setAnimationLoop(null)
|
||
renderer?.dispose();
|
||
scene.children.forEach((obj) => {
|
||
scene.remove(obj);
|
||
})
|
||
camera.remove();
|
||
scene.remove();
|
||
}
|
||
|
||
/** ---------------------------- 设置弹窗配置变化 ---------------------------------- */
|
||
export function changeClearColor(color: 0x000000 | 0xffffff) {
|
||
options.bgColor = color;
|
||
options.contrastColor = options.bgColor === 0x000000 ? 0xffffff : 0x000000;
|
||
scene.background = new THREE.Color(options.bgColor);
|
||
|
||
contrastEntity.forEach(mesh => {
|
||
if (!mesh.material || !mesh.material.color) return;
|
||
|
||
mesh.material.color = new THREE.Color(options.contrastColor);
|
||
})
|
||
} |