diff --git a/src/BatchingKey.js b/src/BatchingKey.js index 77047ef..3a6dd3c 100644 --- a/src/BatchingKey.js +++ b/src/BatchingKey.js @@ -10,12 +10,13 @@ export class BatchingKey { * @param lineType {?number} Line type ID, null for non-lines. Zero is default type (solid * line). */ - constructor(layerName, blockName, geometryType, color, lineType) { + constructor(layerName, blockName, geometryType, color, lineType, vertices) { this.layerName = layerName ?? null this.blockName = blockName ?? null this.geometryType = geometryType ?? null this.color = color this.lineType = lineType ?? null + this.vertices = vertices } /** Comparator function. Fields lexical order corresponds to the constructor arguments order. diff --git a/src/DxfScene.js b/src/DxfScene.js index bece9c4..d861dec 100644 --- a/src/DxfScene.js +++ b/src/DxfScene.js @@ -95,6 +95,7 @@ export class DxfScene { this.pointShapeBlock = null this.numBlocksFlattened = 0 this.numEntitiesFiltered = 0 + this.entityVertices = [] } /** Build the scene from the provided parsed DXF. @@ -277,8 +278,16 @@ export class DxfScene { } } } + + checkTangentLayer (layer) { + const bendKeywords = ['tangent', 'bend_extent']; + return bendKeywords.some(substr=>layer.toLowerCase().indexOf(substr) >= 0); + } _ProcessDxfEntity(entity, blockCtx = null) { + if (this.checkTangentLayer(entity.layer)) { + return; + } let renderEntities switch (entity.type) { case "LINE": @@ -341,6 +350,7 @@ export class DxfScene { * @param blockCtx {?BlockContext} */ _ProcessEntity(entity, blockCtx = null) { + this.entityVertices.push(entity); switch (entity.type) { case Entity.Type.POINTS: this._ProcessPoints(entity, blockCtx) @@ -617,7 +627,7 @@ export class DxfScene { if (isShaped) { /* Shaped mark should be instanced. */ const key = new BatchingKey(layer, POINT_SHAPE_BLOCK_NAME, - BatchingKey.GeometryType.POINT_INSTANCE, color, 0) + BatchingKey.GeometryType.POINT_INSTANCE, color, 0, entity?.vertices ? entity?.vertices : []) const batch = this._GetBatch(key) batch.PushVertex(this._TransformVertex(entity.position)) this._CreatePointShapeBlock() @@ -1452,7 +1462,7 @@ export class DxfScene { } } else { const key = new BatchingKey(layer, entity.name, BatchingKey.GeometryType.BLOCK_INSTANCE, - color, lineType) + color, lineType, entity?.vertices ? entity?.vertices : []) const batch = this._GetBatch(key) batch.PushInstanceTransform(transform) } @@ -1470,7 +1480,7 @@ export class DxfScene { color = blockBatch.key.color } //XXX line type - const key = new BatchingKey(layerName, null, blockBatch.key.geometryType, color, lineType) + const key = new BatchingKey(layerName, null, blockBatch.key.geometryType, color, lineType, []) const batch = this._GetBatch(key) batch.Merge(blockBatch, transform) } @@ -1852,7 +1862,7 @@ export class DxfScene { */ _ProcessPoints(entity, blockCtx = null) { const key = new BatchingKey(entity.layer, blockCtx?.name, - BatchingKey.GeometryType.POINTS, entity.color, 0) + BatchingKey.GeometryType.POINTS, entity.color, 0, entity?.vertices ? entity?.vertices : []) const batch = this._GetBatch(key) for (const v of entity.vertices) { batch.PushVertex(this._TransformVertex(v, blockCtx)) @@ -1868,7 +1878,7 @@ export class DxfScene { throw Error("Even number of vertices expected") } const key = new BatchingKey(entity.layer, blockCtx?.name, - BatchingKey.GeometryType.LINES, entity.color, entity.lineType) + BatchingKey.GeometryType.LINES, entity.color, entity.lineType, entity?.vertices ? entity?.vertices : []) const batch = this._GetBatch(key) for (const v of entity.vertices) { batch.PushVertex(this._TransformVertex(v, blockCtx)) @@ -1890,7 +1900,7 @@ export class DxfScene { if (verticesCount <= 3) { const key = new BatchingKey(entity.layer, blockCtx?.name, BatchingKey.GeometryType.LINES, entity.color, - entity.lineType) + entity.lineType, entity?.vertices ? entity?.vertices : []) const batch = this._GetBatch(key) let prev = null for (const v of entity.vertices) { @@ -1909,7 +1919,7 @@ export class DxfScene { const key = new BatchingKey(entity.layer, blockCtx?.name, BatchingKey.GeometryType.INDEXED_LINES, - entity.color, entity.lineType) + entity.color, entity.lineType, entity?.vertices ? entity?.vertices : []) const batch = this._GetBatch(key) /* Line may be split if exceeds chunk limit. */ for (const lineChunk of entity._IterateLineChunks()) { @@ -1938,7 +1948,7 @@ export class DxfScene { } const key = new BatchingKey(entity.layer, blockCtx?.name, BatchingKey.GeometryType.INDEXED_TRIANGLES, - entity.color, 0) + entity.color, 0, entity?.vertices ? entity?.vertices : []) const batch = this._GetBatch(key) //XXX splitting into chunks is not yet implemented. Currently used only for text glyphs so // should fit into one chunk @@ -2087,7 +2097,8 @@ export class DxfScene { layers: [], origin: this.origin, bounds: this.bounds, - hasMissingChars: this.hasMissingChars + hasMissingChars: this.hasMissingChars, + entityVertices: this.entityVertices } const buffers = { diff --git a/src/DxfViewer.js b/src/DxfViewer.js index 7289e19..4aa846d 100644 --- a/src/DxfViewer.js +++ b/src/DxfViewer.js @@ -21,6 +21,13 @@ export class DxfViewer { * @param options Some options can be overridden if specified. See DxfViewer.DefaultOptions. */ constructor(domContainer, options = null) { + this.dimensions = {dxfWidth: 0, dxfHeight: 0}; + this.totalCuts = 0; + this.cutsLength = 0; + this.area = 0; + this.shapes = []; + this.bends = []; + this.domContainer = domContainer this.options = Object.create(DxfViewer.DefaultOptions) if (options) { @@ -119,6 +126,15 @@ export class DxfViewer { return this.parsedDxf } + GetFileDetails() { + return { + dimensions: this.dimensions, + totalCuts: this.totalCuts, + cutsLength: this.cutsLength, + area: this.area, + } + } + SetSize(width, height) { this._EnsureRenderer() @@ -172,12 +188,17 @@ export class DxfViewer { this.worker = new DxfWorker(workerFactory ? workerFactory() : null) const {scene, dxf} = await this.worker.Load(url, fonts, this.options, progressCbk) + this.SetFileDetails(scene?.entityVertices); await this.worker.Destroy() this.worker = null this.parsedDxf = dxf this.origin = scene.origin this.bounds = scene.bounds + this.dimensions = { + dxfWidth: scene.bounds.maxY - scene.bounds.minY, + dxfHeight: scene.bounds.maxX - scene.bounds.minX + }; this.hasMissingChars = scene.hasMissingChars for (const layer of scene.layers) { @@ -234,6 +255,167 @@ export class DxfViewer { this._EnsureRenderer() this.renderer.render(this.scene, this.camera) } + + checkTangentLayer (layer) { + const bendKeywords = ['tangent', 'bend_extent']; + return bendKeywords.some(substr=>layer.toLowerCase().indexOf(substr) >= 0); + } + + checkBendLayer (layer) { + const bendKeywords = ['bend', 'reference']; + return bendKeywords.some(substr=>layer.toLowerCase().indexOf(substr) >= 0); + } + + _TransformVertex(vs=[]) { + return vs.map(v=>{return { x: v.x - this.origin.x, y: v.y - this.origin.y }}) + } + + getAllLastPoints(entities) { + let points = []; + for (let i = 0; i < entities.length; i++) { + const entity = entities[i]; + if(!this.checkBendLayer(entity.layer) && !entity.lineType && !this.checkTangentLayer(entity.layer)){ + points.push({ + startPointx : entity.vertices[0].x, + startPointy : entity.vertices[0].y, + endPointx : entity.vertices[entity.vertices.length - 1].x, + endPointy : entity.vertices[entity.vertices.length - 1].y, + visited: false, + entity, + points: entity.vertices, + }); + } + } + return points; + } + + SetFileDetails(entityVertices) { + let lastPoints = this.getAllLastPoints(entityVertices); + let cuts = []; + var maxArea = 0; + let cutLength = 0; + + function round3(value){ + return Math.round(value*1000)/1000; + } + + function markPointVisited ({startPointx, startPointy, endPointx, endPointy}) { + const index = lastPoints.findIndex(el=> el.startPointx===startPointx && el.startPointy===startPointy && el.endPointx===endPointx && el.endPointy===endPointy); + if(index >= 0) { + lastPoints[index].visited = true; + } + } + + function visitAllPoints({startPointx, startPointy, endPointx, endPointy}, entity, points) { + markPointVisited({startPointx, startPointy, endPointx, endPointy}); + let allPoints = [...points]; + let ended = false; + let connectedPoints = [entity]; + while(!ended) { + const index = lastPoints.findIndex( + el => + (round3(el.startPointx)===round3(endPointx) && round3(el.startPointy)===round3(endPointy) && !el.visited) || + (round3(el.endPointx)===round3(startPointx) && round3(el.endPointy)===round3(startPointy) && !el.visited) || + (round3(el.endPointx)===round3(endPointx) && round3(el.endPointy)===round3(endPointy) && !el.visited) || + (round3(el.startPointx)===round3(startPointx) && round3(el.startPointy)===round3(startPointy) && !el.visited) + ); + if (index < 0) { + ended = true; + cuts.push(connectedPoints); + } else { + + if(round3(lastPoints[index].startPointx)===round3(endPointx) && round3(lastPoints[index].startPointy)===round3(endPointy)){ + allPoints = [...allPoints, ...lastPoints[index].points]; + } + else if(round3(lastPoints[index].endPointx)===round3(startPointx) && round3(lastPoints[index].endPointy)===round3(startPointy)){ + allPoints = [...lastPoints[index].points, ...allPoints]; + } + else if(round3(lastPoints[index].endPointx)===round3(endPointx) && round3(lastPoints[index].endPointy)===round3(endPointy)){ + allPoints = [ ...allPoints, ...lastPoints[index].points.reverse()]; + } + else if(round3(lastPoints[index].startPointx)===round3(startPointx) && round3(lastPoints[index].startPointy)===round3(startPointy)){ + allPoints = [...lastPoints[index].points.reverse(), ...allPoints]; + } + + startPointx = allPoints[0].x; + startPointy = allPoints[0].y; + endPointx = allPoints[allPoints.length - 1].x; + endPointy = allPoints[allPoints.length - 1].y; + entity = lastPoints[index].entity; + lastPoints[index].visited = true; + connectedPoints.push(lastPoints[index].entity); + } + } + return allPoints.map(el=>new three.Vector2(el.x, el.y)); + } + + function isPointVisited({startPointx, startPointy, endPointx, endPointy}) { + const found = lastPoints.find(el=> el.startPointx===startPointx && el.startPointy===startPointy && el.endPointx===endPointx && el.endPointy===endPointy); + if(found) { + return found.visited; + } + else { + return true; + } + } + + for (let i = 0; i < entityVertices.length; i++) { + const entity = entityVertices[i]; + if(!this.checkBendLayer(entity.layer) && !entity.lineType && !this.checkTangentLayer(entity.layer) && !isPointVisited({ + startPointx : entity.vertices[0].x, + startPointy : entity.vertices[0].y, + endPointx : entity.vertices[entity.vertices.length - 1].x, + endPointy : entity.vertices[entity.vertices.length - 1].y, + })) { + const points = visitAllPoints({ + startPointx : entity.vertices[0].x, + startPointy : entity.vertices[0].y, + endPointx : entity.vertices[entity.vertices.length - 1].x, + endPointy : entity.vertices[entity.vertices.length - 1].y, + }, entity, entity.vertices); + this.shapes.push(points); + cutLength += this.getVectorDistance(points); + const area = this.calcPolygonArea(points); + if (area > maxArea){ + maxArea = area; + } + } else if(this.checkBendLayer(entity.layer)) { + this.bends.push(entity.vertices.map(el=>new three.Vector2(el.x, el.y))); + } + } + + console.log("TOTAL LENGTH : " + cutLength.toFixed(3)); + console.log("TOTAL CUTS : " + cuts.length); + console.log("TOTAL AREA : " + maxArea.toFixed(3)); + + this.totalCuts = cuts.length; + this.cutsLength = cutLength; + this.area = maxArea; + } + + getVectorDistance(vectors) { + let sum=0; + for(let i=0;i} List of layer names. */ GetLayers() {