import * as THREE from "../../libs/three.js/build/three.module.js"; import '../../libs/three.js/loaders/dxf/dxf-parser.js' export default class DxfLoader extends THREE.EventDispatcher{ constructor(){ super() this.parser = new Potree.DxfParser } load(url,done, options={}){ Potree.loadFile(url, {returnText:true} , (data)=>{ var dxfJson = this.parser.parseSync(data); console.log(dxfJson) let model = this.createModel(dxfJson, options) done(model) }) } createModel(data, options={}){ let root = new THREE.Object3D; var i, entity, obj, min_x, min_y, min_z, max_x, max_y, max_z; /* var dims = { min: { x: false, y: false, z: false}, max: { x: false, y: false, z: false} }; */ //let bound = THREE.Box3 for(i = 0; i < data.entities.length; i++) { entity = data.entities[i]; obj = drawEntity(entity, data, options.unsupportTypes ); if (obj) { var bbox = new THREE.Box3().setFromObject(obj); /* if (bbox.min.x && ((dims.min.x === false) || (dims.min.x > bbox.min.x))) dims.min.x = bbox.min.x; if (bbox.min.y && ((dims.min.y === false) || (dims.min.y > bbox.min.y))) dims.min.y = bbox.min.y; if (bbox.min.z && ((dims.min.z === false) || (dims.min.z > bbox.min.z))) dims.min.z = bbox.min.z; if (bbox.max.x && ((dims.max.x === false) || (dims.max.x < bbox.max.x))) dims.max.x = bbox.max.x; if (bbox.max.y && ((dims.max.y === false) || (dims.max.y < bbox.max.y))) dims.max.y = bbox.max.y; if (bbox.max.z && ((dims.max.z === false) || (dims.max.z < bbox.max.z))) dims.max.z = bbox.max.z; */ root.add(obj); } obj = null; } return root } } function drawEntity(entity, data, unsupportTypes, group) { var mesh; if(unsupportTypes && unsupportTypes.includes(entity.type)){ console.warn('遇到一个dxf不会绘制的type:', entity.type) return //add 不绘制该项 } if(entity.type === 'CIRCLE' || entity.type === 'ARC') { mesh = drawArc(entity, data, unsupportTypes, group ); } else if(entity.type === 'LWPOLYLINE' || entity.type === 'LINE' || entity.type === 'POLYLINE') { mesh = drawLine(entity, data, unsupportTypes, group ); } else if(entity.type === 'TEXT') {//"MTEXT" text: "{\fKaiTi|b0|i0|c134|p49;糕点制作台}" mesh = drawText(entity, data, unsupportTypes, group); } else if(entity.type === 'SOLID') { mesh = drawSolid(entity, data); } else if(entity.type === 'POINT') { mesh = drawPoint(entity, data ); } else if(entity.type === 'INSERT') {//嵌套其他group mesh = drawBlock(entity, data, unsupportTypes, group ); } else if(entity.type === 'SPLINE') { mesh = drawSpline(entity, data); } else if(entity.type === 'MTEXT') { mesh = drawMtext(entity, data, unsupportTypes, group ); } else if(entity.type === 'ELLIPSE') { mesh = drawEllipse(entity, data, unsupportTypes, group ); } else if(entity.type === 'DIMENSION') { var dimTypeEnum = entity.dimensionType & 7; if(dimTypeEnum === 0) { mesh = drawDimension(entity, data); } else { console.log("Unsupported Dimension type: " + dimTypeEnum); } } else { console.log("Unsupported Entity Type: " + entity.type); } if(entity.type === 'LWPOLYLINE' || entity.type === 'LINE' || entity.type === 'POLYLINE') { mesh = drawLine(entity, data); } /* if(entity.type === 'INSERT') { mesh = drawBlock(entity, data); //group } */ /* 门都是arc 似乎墙壁都是不是insert类型,但窗户是 */ mesh && (mesh.dxfInfo = entity) //add return mesh; } function drawEllipse(entity, data) { var color = getColor(entity, data); var xrad = Math.sqrt(Math.pow(entity.majorAxisEndPoint.x,2) + Math.pow(entity.majorAxisEndPoint.y,2)); var yrad = xrad*entity.axisRatio; var rotation = Math.atan2(entity.majorAxisEndPoint.y, entity.majorAxisEndPoint.x); var curve = new THREE.EllipseCurve( entity.center.x, entity.center.y, xrad, yrad, entity.startAngle, entity.endAngle, false, // Always counterclockwise rotation ); var points = curve.getPoints( 50 ); var geometry = new THREE.BufferGeometry().setFromPoints( points ); var material = new THREE.LineBasicMaterial( { linewidth: 1, color : color } ); // Create the final object to add to the scene var ellipse = new THREE.Line( geometry, material ); return ellipse; } function drawMtext(entity, data) { var color = getColor(entity, data); //if (!font) { return console.log('font parameter not set. Ignoring text entity.')} var geometry = new THREE.TextGeometry( entity.text, { //font: font, size: entity.height * (4/5), height: 1 }); var material = new THREE.MeshBasicMaterial( {color: color} ); var text = new THREE.Mesh( geometry, material ); // Measure what we rendered. var measure = new THREE.Box3(); measure.setFromObject( text ); var textWidth = measure.max.x - measure.min.x; // If the text ends up being wider than the box, it's supposed // to be multiline. Doing that in threeJS is overkill. if (textWidth > entity.width) { console.log("Can't render this multipline MTEXT entity, sorry.", entity); return undefined; } text.position.z = 0; switch (entity.attachmentPoint) { case 1: // Top Left text.position.x = entity.position.x; text.position.y = entity.position.y - entity.height; break; case 2: // Top Center text.position.x = entity.position.x - textWidth/2; text.position.y = entity.position.y - entity.height; break; case 3: // Top Right text.position.x = entity.position.x - textWidth; text.position.y = entity.position.y - entity.height; break; case 4: // Middle Left text.position.x = entity.position.x; text.position.y = entity.position.y - entity.height/2; break; case 5: // Middle Center text.position.x = entity.position.x - textWidth/2; text.position.y = entity.position.y - entity.height/2; break; case 6: // Middle Right text.position.x = entity.position.x - textWidth; text.position.y = entity.position.y - entity.height/2; break; case 7: // Bottom Left text.position.x = entity.position.x; text.position.y = entity.position.y; break; case 8: // Bottom Center text.position.x = entity.position.x - textWidth/2; text.position.y = entity.position.y; break; case 9: // Bottom Right text.position.x = entity.position.x - textWidth; text.position.y = entity.position.y; break; default: return undefined; }; return text; } function drawSpline(entity, data) { var color = getColor(entity, data); var points = entity.controlPoints.map(function(vec) { return new THREE.Vector2(vec.x, vec.y); }); var interpolatedPoints = []; var curve; if (entity.degreeOfSplineCurve === 2 || entity.degreeOfSplineCurve === 3) { for(var i = 0; i + 2 < points.length; i = i + 2) { if (entity.degreeOfSplineCurve === 2) { curve = new THREE.QuadraticBezierCurve(points[i], points[i + 1], points[i + 2]); } else { curve = new THREE.QuadraticBezierCurve3(points[i], points[i + 1], points[i + 2]); } interpolatedPoints.push.apply(interpolatedPoints, curve.getPoints(50)); } } else { curve = new THREE.SplineCurve(points); interpolatedPoints = curve.getPoints( 100 ); } var geometry = new THREE.BufferGeometry().setFromPoints( interpolatedPoints ); var material = new THREE.LineBasicMaterial( { linewidth: 1, color : color } ); var splineObject = new THREE.Line( geometry, material ); return splineObject; } function drawLine(entity, data) { var geometry = new THREE.Geometry(), color = getColor(entity, data), material, lineType, vertex, startPoint, endPoint, bulgeGeometry, bulge, i, line; if (!entity.vertices) return console.log('entity missing vertices.'); // create geometry 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] : geometry.vertices[0]; bulgeGeometry = new THREEx.BulgeGeometry(startPoint, endPoint, bulge); geometry.vertices.push.apply(geometry.vertices, bulgeGeometry.vertices); } else { vertex = entity.vertices[i]; geometry.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0)); } } if(entity.shape) geometry.vertices.push(geometry.vertices[0]); // set material 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 }); } // if(lineType && lineType.pattern && lineType.pattern.length !== 0) { // geometry.computeLineDistances(); // // Ugly hack to add diffuse to this. Maybe copy the uniforms object so we // // don't add diffuse to a material. // lineType.material.uniforms.diffuse = { type: 'c', value: new THREE.Color(color) }; // material = new THREE.ShaderMaterial({ // uniforms: lineType.material.uniforms, // vertexShader: lineType.material.vertexShader, // fragmentShader: lineType.material.fragmentShader // }); // }else { // material = new THREE.LineBasicMaterial({ linewidth: 1, color: color }); // } line = new THREE.Line(geometry, material); return line; } function drawArc(entity, data) { var startAngle, endAngle; if (entity.type === 'CIRCLE') { startAngle = entity.startAngle || 0; endAngle = startAngle + 2 * Math.PI; } else { startAngle = entity.startAngle; endAngle = entity.endAngle; } var curve = new THREE.ArcCurve( 0, 0, entity.radius, startAngle, endAngle); var points = curve.getPoints( 32 ); var geometry = new THREE.BufferGeometry().setFromPoints( points ); var material = new THREE.LineBasicMaterial({ color: getColor(entity, data) }); var 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 drawSolid(entity, data) { var material, mesh, verts, geometry = new THREE.Geometry(); verts = geometry.vertices; verts.push(new THREE.Vector3(entity.points[0].x, entity.points[0].y, entity.points[0].z)); verts.push(new THREE.Vector3(entity.points[1].x, entity.points[1].y, entity.points[1].z)); verts.push(new THREE.Vector3(entity.points[2].x, entity.points[2].y, entity.points[2].z)); verts.push(new THREE.Vector3(entity.points[3].x, entity.points[3].y, entity.points[3].z)); // Calculate which direction the points are facing (clockwise or counter-clockwise) var vector1 = new THREE.Vector3(); var vector2 = new THREE.Vector3(); vector1.subVectors(verts[1], verts[0]); vector2.subVectors(verts[2], verts[0]); vector1.cross(vector2); // If z < 0 then we must draw these in reverse order if(vector1.z < 0) { geometry.faces.push(new THREE.Face3(2, 1, 0)); geometry.faces.push(new THREE.Face3(2, 3, 1)); } else { geometry.faces.push(new THREE.Face3(0, 1, 2)); geometry.faces.push(new THREE.Face3(1, 3, 2)); } material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) }); return new THREE.Mesh(geometry, material); } function drawText(entity, data) { var geometry, material, text; //必须要font才能画出文字 /* if(!font) return console.warn('Text is not supported without a Three.js font loaded with THREE.FontLoader! Load a font of your choice and pass this into the constructor. See the sample for this repository or Three.js examples at http://threejs.org/examples/?q=text#webgl_geometry_text for more details.'); */ geometry = new THREE.TextGeometry(entity.text, {/* font: font, */ height: 0, size: entity.textHeight || 12 }); if (entity.rotation) { var zRotation = entity.rotation * Math.PI / 180; geometry.rotateZ(zRotation); } material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) }); 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 drawPoint(entity, data) { var geometry, material, point; geometry = new THREE.Geometry(); geometry.vertices.push(new THREE.Vector3(entity.position.x, entity.position.y, entity.position.z)); // TODO: could be more efficient. PointCloud per layer? var numPoints = 1; var color = getColor(entity, data); var colors = new Float32Array( numPoints*3 ); colors[0] = color.r; colors[1] = color.g; colors[2] = color.b; geometry.colors = colors; geometry.computeBoundingBox(); material = new THREE.PointsMaterial( { size: 0.05, vertexColors: THREE.VertexColors } ); point = new THREE.Points(geometry, material); //scene.add(point); return point } function drawDimension(entity, data, unsupportTypes, parentGroup) { return drawBlock(entity, data, unsupportTypes, parentGroup, true) //改 /* var block = data.blocks[entity.block]; if (!block || !block.entities) return null; var group = new THREE.Object3D(); // if(entity.anchorPoint) { // group.position.x = entity.anchorPoint.x; // group.position.y = entity.anchorPoint.y; // group.position.z = entity.anchorPoint.z; // } for(var i = 0; i < block.entities.length; i++) { var childEntity = drawEntity(block.entities[i], data, group); if(childEntity && !group.children.includes(childEntity)) group.add(childEntity); } return group; */ } function drawBlock(entity, data, unsupportTypes, parentGroup, isDimension) { var block = data.blocks[entity.name]; if (!block.entities) return null; var group = new THREE.Object3D() parentGroup && parentGroup.add(group)//提前添加 为了获得matrixWorld if(!isDimension){ if(entity.position) { group.position.x = entity.position.x; group.position.y = entity.position.y; group.position.z = entity.position.z; } 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; } //add: 经常看到有的会脱离主区域很远 add zScale var flag; if(entity.zScale != void 0 && entity.extrusionDirection){//see (005.dxf、 others/01.dxf) extrusionDirection ={x:0,y:0,z:-1} group.position.x *= Math.sign(entity.zScale) group.scale.x *= Math.sign(entity.zScale); group.rotation.z *= Math.sign(entity.zScale) flag = 1; //检查下其他的extrusionDirection } }else{ /* if(entity.anchorPoint) { group.position.x = entity.anchorPoint.x; group.position.y = entity.anchorPoint.y; group.position.z = entity.anchorPoint.z; } */ } //add: group.updateMatrixWorld() var visiEntities = block.entities.filter(e=>e.visible !== false) //console.log("block的可见children个数"+block.entities.length) for(var i = 0; i < visiEntities.length; i++) { var childEntity = drawEntity(visiEntities[i], data, unsupportTypes, group ); if(childEntity && !group.children.includes(childEntity)) group.add(childEntity); } return group; } function getColor(entity, data) { var color = 0x000000; //default if(entity.color) color = entity.color; else if(data.tables && data.tables.layer && data.tables.layer.layers[entity.layer]) color = data.tables.layer.layers[entity.layer].color; if(color == null || color === 0xffffff) { color = 0x000000; } return color; } function createLineTypeShaders(data) { var ltype, type; if(!data.tables || !data.tables.lineType) return; var ltypes = data.tables.lineType.lineTypes; for(type in ltypes) { ltype = ltypes[type]; if(!ltype.pattern) continue; ltype.material = createDashedLineShader(ltype.pattern); } } function createDashedLineShader(pattern) { var i, dashedLineShader = {}, totalLength = 0.0; for(i = 0; i < pattern.length; i++) { totalLength += Math.abs(pattern[i]); } dashedLineShader.uniforms = THREE.UniformsUtils.merge([ THREE.UniformsLib[ 'common' ], THREE.UniformsLib[ 'fog' ], { 'pattern': { type: 'fv1', value: pattern }, '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 findExtents(scene) { for(var child of scene.children) { var minX, maxX, minY, maxY; if(child.position) { minX = Math.min(child.position.x, minX); minY = Math.min(child.position.y, minY); maxX = Math.max(child.position.x, maxX); maxY = Math.max(child.position.y, maxY); } } return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY }}; } // Three.js extension functions. Webpack doesn't seem to like it if we modify the THREE object directly. var THREEx = { Math: {} }; /** * Returns the angle in radians of the vector (p1,p2). In other words, imagine * putting the base of the vector at coordinates (0,0) and finding the angle * from vector (1,0) to (p1,p2). * @param {Object} p1 start point of the vector * @param {Object} p2 end point of the vector * @return {Number} the angle */ THREEx.Math.angle2 = function(p1, p2) { var v1 = new THREE.Vector2(p1.x, p1.y); var v2 = new THREE.Vector2(p2.x, p2.y); v2.sub(v1); // sets v2 to be our chord v2.normalize(); if(v2.y < 0) return -Math.acos(v2.x); return Math.acos(v2.x); }; THREEx.Math.polar = function(point, distance, angle) { var result = {}; result.x = point.x + distance * Math.cos(angle); result.y = point.y + distance * Math.sin(angle); return result; }; /** * Calculates points for a curve between two points * @param startPoint - the starting point of the curve * @param endPoint - the ending point of the curve * @param bulge - a value indicating how much to curve * @param segments - number of segments between the two given points */ THREEx.BulgeGeometry = function ( startPoint, endPoint, bulge, segments ) { var vertex, i, center, p0, p1, angle, radius, startAngle, thetaAngle; THREE.Geometry.call( this ); this.startPoint = p0 = startPoint ? new THREE.Vector2(startPoint.x, startPoint.y) : new THREE.Vector2(0,0); this.endPoint = p1 = endPoint ? new THREE.Vector2(endPoint.x, endPoint.y) : new THREE.Vector2(1,0); this.bulge = bulge = bulge || 1; angle = 4 * Math.atan(bulge); radius = p0.distanceTo(p1) / 2 / Math.sin(angle/2); center = THREEx.Math.polar(startPoint, radius, THREEx.Math.angle2(p0,p1) + (Math.PI / 2 - angle/2)); this.segments = segments = segments || Math.max( Math.abs(Math.ceil(angle/(Math.PI/18))), 6); // By default want a segment roughly every 10 degrees startAngle = THREEx.Math.angle2(center, p0); thetaAngle = angle / segments; this.vertices.push(new THREE.Vector3(p0.x, p0.y, 0)); for(i = 1; i <= segments - 1; i++) { vertex = THREEx.Math.polar(center, Math.abs(radius), startAngle + thetaAngle * i); this.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0)); } }; THREEx.BulgeGeometry.prototype = Object.create( THREE.Geometry.prototype ); /* -----note-------------- */