491 lines
10 KiB
TypeScript
491 lines
10 KiB
TypeScript
|
export class DxfWritor
|
||
|
{
|
||
|
layers: WeakSet<Layer>
|
||
|
activeLayer: Layer
|
||
|
lineTypes: WeakSet<LineType>
|
||
|
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
|
||
|
}
|
||
|
}
|