export class DxfWritor { layers: WeakSet activeLayer: Layer lineTypes: WeakSet offX = 0 offY = 0 // 基点 constructor() { this.layers = new WeakSet() this.activeLayer = null this.lineTypes = new WeakSet() for (let i = 0; i < DrawingType.LINE_TYPES.length; ++i) { this.addLineType( DrawingType.LINE_TYPES[i].name, DrawingType.LINE_TYPES[i].description, DrawingType.LINE_TYPES[i].elements, ) } for (let i = 0; i < DrawingType.LAYERS.length; ++i) { this.addLayer( DrawingType.LAYERS[i].name, DrawingType.LAYERS[i].colorNumber, DrawingType.LAYERS[i].lineTypeName, ) } this.setActiveLayer('0') } /** * @param {string} name * @param {string} description * @param {Array} elements - if elem > 0 it is a line, if elem < 0 it is gap, if elem == 0.0 it is a */ addLineType(name, description, elements) { this.lineTypes[name] = new LineType(name, description, elements) return this } addLayer(name, colorNumber, lineTypeName) { this.layers[name] = new Layer(name, colorNumber, lineTypeName) return this } /** 设置当前图层 白=0, 红=1, 黄=2, 绿=3,CYAN=4,BLUE=5,MAGENTA=6 */ setActiveLayer(name) { this.activeLayer = this.layers[name] return this } setOffset(x1, y1) { this.offX = x1 this.offY = y1 } clear() { } drawLine(x1, y1, x2, y2) { this.activeLayer.addShape(new Line(x1 + this.offX, y1 + this.offY, x2 + this.offX, y2 + this.offY)) return this } drawRect(x1, y1, w, h) { x1 = x1 + this.offX y1 = y1 + this.offY this.activeLayer.addShape(new Line(x1, y1, x1 + w, y1)) this.activeLayer.addShape(new Line(x1 + w, y1, x1 + w, y1 + h)) this.activeLayer.addShape(new Line(x1 + w, y1 + h, x1, y1 + h)) this.activeLayer.addShape(new Line(x1, y1 + h, x1, y1)) return this } /** * @param {number} x1 - Center x * @param {number} y1 - Center y * @param {number} r - radius * @param {number} startAngle - degree * @param {number} endAngle - degree */ drawArc(x1, y1, r, startAngle, endAngle) { this.activeLayer.addShape(new Arc(x1 + this.offX, y1 + this.offY, r, startAngle, endAngle)) return this } /** * @param {number} x1 - Center x * @param {number} y1 - Center y * @param {number} r - radius */ drawCircle(x1, y1, r) { this.activeLayer.addShape(new Circle(x1 + this.offX, y1 + this.offY, r)) return this } /** * @param {number} x1 - x * @param {number} y1 - y * @param {number} height - Text height * @param {number} rotation - Text rotation * @param {string} value - the string itself */ drawText(x1, y1, height, rotation, value) { this.activeLayer.addShape(new Text(x1 + this.offX, y1 + this.offY, height, rotation, value)) return this } /** * @param {Array} points - Array of points like [ [x1, y1,r], [x2, y2,r]... ] */ drawPolyline(points, isClose = false) { for (const p of points) { p.x += this.offX p.y += this.offY } this.activeLayer.addShape(new Polyline(points, isClose)) return this } _getDxfLtypeTable() { let s = '0\nTABLE\n' // start table s += '2\nLTYPE\n' // name table as LTYPE table for (let lineTypeName in this.lineTypes) { s += this.lineTypes[lineTypeName].toDxfString() } s += '0\nENDTAB\n' // end table return s } _getDxfLayerTable() { let s = '0\nTABLE\n' // start table s += '2\nLAYER\n' // name table as LAYER table for (let layerName in this.layers) { s += this.layers[layerName].toDxfString() } s += '0\nENDTAB\n' return s } toDxfString() { let s = '' // start section s += '0\nSECTION\n' // name section as TABLES section s += '2\nTABLES\n' s += this._getDxfLtypeTable() s += this._getDxfLayerTable() // end section s += '0\nENDSEC\n' // ENTITES section s += '0\nSECTION\n' s += '2\nENTITIES\n' for (let layerName in this.layers) { let layer = this.layers[layerName] s += layer.shapesToDxf() // let shapes = layer.getShapes(); } s += '0\nENDSEC\n' // close file s += '0\nEOF' return s } } namespace DrawingType { // AutoCAD Color Index (ACI) // http://sub-atomic.com/~moses/acadcolors.html export const ACI = { LAYER: 0, RED: 1, YELLOW: 2, GREEN: 3, CYAN: 4, BLUE: 5, MAGENTA: 6, WHITE: 7, } export const LINE_TYPES = [ { name: 'CONTINUOUS', description: '______', elements: [] }, { name: 'DASHED', description: '_ _ _ ', elements: [5.0, -5.0] }, { name: 'DOTTED', description: '. . . ', elements: [0.0, -5.0] }, ] export const LAYERS = [ { name: '0', colorNumber: ACI.WHITE, lineTypeName: 'CONTINUOUS' }, { name: '1', colorNumber: ACI.RED, lineTypeName: 'CONTINUOUS' }, { name: '2', colorNumber: ACI.YELLOW, lineTypeName: 'CONTINUOUS' }, { name: '3', colorNumber: ACI.GREEN, lineTypeName: 'CONTINUOUS' }, { name: '4', colorNumber: ACI.CYAN, lineTypeName: 'CONTINUOUS' }, { name: '5', colorNumber: ACI.BLUE, lineTypeName: 'CONTINUOUS' }, { name: '6', colorNumber: ACI.MAGENTA, lineTypeName: 'CONTINUOUS' }, ] } export class LineType { name: string description: string elements: any[] /** * @param {string} name * @param {string} description * @param {Array} elements - if elem > 0 it is a line, if elem < 0 it is gap, if elem == 0.0 it is a */ constructor(name, description, elements) { this.name = name this.description = description this.elements = elements } /** * @link https://www.autodesk.com/techpubs/autocad/acadr14/dxf/ltype_al_u05_c.htm */ toDxfString() { let s = '0\nLTYPE\n' s += '72\n65\n' s += '70\n64\n' s += `2\n${this.name}\n` s += `3\n${this.description}\n` s += `73\n${this.elements.length}\n` s += `40\n${this.getElementsSum()}\n` for (let i = 0; i < this.elements.length; ++i) { s += `49\n${this.elements[i]}\n` } return s } getElementsSum() { let sum = 0 for (let i = 0; i < this.elements.length; ++i) { sum += Math.abs(this.elements[i]) } return sum } } export class Layer { name: string colorNumber: string lineTypeName: string shapes: any[] constructor(name, colorNumber, lineTypeName) { this.name = name this.colorNumber = colorNumber this.lineTypeName = lineTypeName this.shapes = [] } toDxfString() { let s = '0\nLAYER\n' s += '70\n64\n' s += `2\n${this.name}\n` s += `62\n${this.colorNumber}\n` s += `6\n${this.lineTypeName}\n` return s } addShape(shape) { this.shapes.push(shape) shape.layer = this } getShapes() { return this.shapes } shapesToDxf() { let s = '' for (let i = 0; i < this.shapes.length; ++i) { s += this.shapes[i].toDxfString() } return s } } export class Arc { x1: number y1: number r: number startAngle: number endAngle: number layer: Layer /** * @param {number} x1 - Center x * @param {number} y1 - Center y * @param {number} r - radius * @param {number} startAngle - degree * @param {number} endAngle - degree */ constructor(x1, y1, r, startAngle, endAngle) { this.x1 = x1 this.y1 = y1 this.r = r this.startAngle = startAngle this.endAngle = endAngle } toDxfString() { // https://www.autodesk.com/techpubs/autocad/acadr14/dxf/line_al_u05_c.htm let s = `0\nARC\n` s += `8\n${this.layer.name}\n` s += `10\n${this.x1}\n20\n${this.y1}\n30\n0\n` s += `40\n${this.r}\n50\n${this.startAngle}\n51\n${this.endAngle}\n` return s } } export class Circle { x1: number y1: number r: number layer: Layer /** * @param {number} x1 - Center x * @param {number} y1 - Center y * @param {number} r - radius */ constructor(x1, y1, r) { this.x1 = x1 this.y1 = y1 this.r = r } toDxfString() { // https://www.autodesk.com/techpubs/autocad/acadr14/dxf/circle_al_u05_c.htm let s = `0\nCIRCLE\n` s += `8\n${this.layer.name}\n` s += `10\n${this.x1}\n20\n${this.y1}\n30\n0\n` s += `40\n${this.r}\n` return s } } export class Line { x1: number y1: number x2: number y2: number layer: Layer constructor(x1: number, y1: number, x2: number, y2: number) { this.x1 = x1 this.y1 = y1 this.x2 = x2 this.y2 = y2 } toDxfString() { // https://www.autodesk.com/techpubs/autocad/acadr14/dxf/line_al_u05_c.htm let s = `0\nLINE\n` s += `8\n${this.layer.name}\n` s += `10\n${this.x1}\n20\n${this.y1}\n30\n0\n` s += `11\n${this.x2}\n21\n${this.y2}\n31\n0\n` return s } } export class Polyline { points: any[] isClose = false layer: Layer /** * @param {Array} points - Array of points like [ [x1, y1,r], [x2, y2,r]... ] */ constructor(points, isClose) { this.points = points this.isClose = isClose } toDxfString() { // https://www.autodesk.com/techpubs/autocad/acad2000/dxf/polyline_dxf_06.htm // https://www.autodesk.com/techpubs/autocad/acad2000/dxf/vertex_dxf_06.htm let s = `0\nPOLYLINE\n` s += `8\n${this.layer.name}\n` s += `66\n1\n70\n${this.isClose ? 1 : 0}\n` for (let i = 0; i < this.points.length; ++i) { s += `0\nVERTEX\n` s += `8\n${this.layer.name}\n` s += `70\n0\n` s += `10\n${this.points[i].x}\n20\n${this.points[i].y}\n42\n${this.points[i].r}\n` } s += `0\nSEQEND\n` return s } } export class Text { x1: number y1: number height: number rotation: number value: string layer: Layer /** * @param {number} x1 - x * @param {number} y1 - y * @param {number} height - Text height * @param {number} rotation - Text rotation * @param {string} value - the string itself */ constructor(x1, y1, height, rotation, value) { this.x1 = x1 this.y1 = y1 this.height = height this.rotation = rotation this.value = value } toDxfString() { // https://www.autodesk.com/techpubs/autocad/acadr14/dxf/text_al_u05_c.htm let s = `0\nTEXT\n` s += `8\n${this.layer.name}\n` s += `1\n${this.value}\n` s += `10\n${this.x1}\n20\n${this.y1}\n30\n0\n` s += `40\n${this.height}\n50\n${this.rotation}\n` return s } }