feat(All):Initial
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* AutoCad files sometimes use an indexed color value between 1 and 255 inclusive.
|
||||
* Each value corresponds to a color. index 1 is red, that is 16711680 or 0xFF0000.
|
||||
* index 0 and 256, while included in this array, are actually reserved for inheritance
|
||||
* values in AutoCad so they should not be used for index color lookups.
|
||||
*/
|
||||
|
||||
export default [
|
||||
0,
|
||||
16711680,
|
||||
16776960,
|
||||
65280,
|
||||
65535,
|
||||
255,
|
||||
16711935,
|
||||
16777215,
|
||||
8421504,
|
||||
12632256,
|
||||
16711680,
|
||||
16744319,
|
||||
13369344,
|
||||
13395558,
|
||||
10027008,
|
||||
10046540,
|
||||
8323072,
|
||||
8339263,
|
||||
4980736,
|
||||
4990502,
|
||||
16727808,
|
||||
16752511,
|
||||
13382400,
|
||||
13401958,
|
||||
10036736,
|
||||
10051404,
|
||||
8331008,
|
||||
8343359,
|
||||
4985600,
|
||||
4992806,
|
||||
16744192,
|
||||
16760703,
|
||||
13395456,
|
||||
13408614,
|
||||
10046464,
|
||||
10056268,
|
||||
8339200,
|
||||
8347455,
|
||||
4990464,
|
||||
4995366,
|
||||
16760576,
|
||||
16768895,
|
||||
13408512,
|
||||
13415014,
|
||||
10056192,
|
||||
10061132,
|
||||
8347392,
|
||||
8351551,
|
||||
4995328,
|
||||
4997670,
|
||||
16776960,
|
||||
16777087,
|
||||
13421568,
|
||||
13421670,
|
||||
10000384,
|
||||
10000460,
|
||||
8355584,
|
||||
8355647,
|
||||
5000192,
|
||||
5000230,
|
||||
12582656,
|
||||
14679935,
|
||||
10079232,
|
||||
11717734,
|
||||
7510016,
|
||||
8755276,
|
||||
6258432,
|
||||
7307071,
|
||||
3755008,
|
||||
4344870,
|
||||
8388352,
|
||||
12582783,
|
||||
6736896,
|
||||
10079334,
|
||||
5019648,
|
||||
7510092,
|
||||
4161280,
|
||||
6258495,
|
||||
2509824,
|
||||
3755046,
|
||||
4194048,
|
||||
10485631,
|
||||
3394560,
|
||||
8375398,
|
||||
2529280,
|
||||
6264908,
|
||||
2064128,
|
||||
5209919,
|
||||
1264640,
|
||||
3099686,
|
||||
65280,
|
||||
8388479,
|
||||
52224,
|
||||
6736998,
|
||||
38912,
|
||||
5019724,
|
||||
32512,
|
||||
4161343,
|
||||
19456,
|
||||
2509862,
|
||||
65343,
|
||||
8388511,
|
||||
52275,
|
||||
6737023,
|
||||
38950,
|
||||
5019743,
|
||||
32543,
|
||||
4161359,
|
||||
19475,
|
||||
2509871,
|
||||
65407,
|
||||
8388543,
|
||||
52326,
|
||||
6737049,
|
||||
38988,
|
||||
5019762,
|
||||
32575,
|
||||
4161375,
|
||||
19494,
|
||||
2509881,
|
||||
65471,
|
||||
8388575,
|
||||
52377,
|
||||
6737074,
|
||||
39026,
|
||||
5019781,
|
||||
32607,
|
||||
4161391,
|
||||
19513,
|
||||
2509890,
|
||||
65535,
|
||||
8388607,
|
||||
52428,
|
||||
6737100,
|
||||
39064,
|
||||
5019800,
|
||||
32639,
|
||||
4161407,
|
||||
19532,
|
||||
2509900,
|
||||
49151,
|
||||
8380415,
|
||||
39372,
|
||||
6730444,
|
||||
29336,
|
||||
5014936,
|
||||
24447,
|
||||
4157311,
|
||||
14668,
|
||||
2507340,
|
||||
32767,
|
||||
8372223,
|
||||
26316,
|
||||
6724044,
|
||||
19608,
|
||||
5010072,
|
||||
16255,
|
||||
4153215,
|
||||
9804,
|
||||
2505036,
|
||||
16383,
|
||||
8364031,
|
||||
13260,
|
||||
6717388,
|
||||
9880,
|
||||
5005208,
|
||||
8063,
|
||||
4149119,
|
||||
4940,
|
||||
2502476,
|
||||
255,
|
||||
8355839,
|
||||
204,
|
||||
6710988,
|
||||
152,
|
||||
5000344,
|
||||
127,
|
||||
4145023,
|
||||
76,
|
||||
2500172,
|
||||
4129023,
|
||||
10452991,
|
||||
3342540,
|
||||
8349388,
|
||||
2490520,
|
||||
6245528,
|
||||
2031743,
|
||||
5193599,
|
||||
1245260,
|
||||
3089996,
|
||||
8323327,
|
||||
12550143,
|
||||
6684876,
|
||||
10053324,
|
||||
4980888,
|
||||
7490712,
|
||||
4128895,
|
||||
6242175,
|
||||
2490444,
|
||||
3745356,
|
||||
12517631,
|
||||
14647295,
|
||||
10027212,
|
||||
11691724,
|
||||
7471256,
|
||||
8735896,
|
||||
6226047,
|
||||
7290751,
|
||||
3735628,
|
||||
4335180,
|
||||
16711935,
|
||||
16744447,
|
||||
13369548,
|
||||
13395660,
|
||||
9961624,
|
||||
9981080,
|
||||
8323199,
|
||||
8339327,
|
||||
4980812,
|
||||
4990540,
|
||||
16711871,
|
||||
16744415,
|
||||
13369497,
|
||||
13395634,
|
||||
9961586,
|
||||
9981061,
|
||||
8323167,
|
||||
8339311,
|
||||
4980793,
|
||||
4990530,
|
||||
16711807,
|
||||
16744383,
|
||||
13369446,
|
||||
13395609,
|
||||
9961548,
|
||||
9981042,
|
||||
8323135,
|
||||
8339295,
|
||||
4980774,
|
||||
4990521,
|
||||
16711743,
|
||||
16744351,
|
||||
13369395,
|
||||
13395583,
|
||||
9961510,
|
||||
9981023,
|
||||
8323103,
|
||||
8339279,
|
||||
4980755,
|
||||
4990511,
|
||||
3355443,
|
||||
5987163,
|
||||
8684676,
|
||||
11382189,
|
||||
14079702,
|
||||
16777215
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
// 维度样式变量可以在DIMSTYLE表中使用,也可以在XDATA中的Dimension实体样式覆盖中使用。
|
||||
const codes = new Map([
|
||||
[140, "DIMTXT"],
|
||||
[142, "DIMTSZ"],
|
||||
[144, "DIMLFAC"],
|
||||
[147, "DIMGAP"],
|
||||
[173, "DIMSAH"],
|
||||
[175, "DIMSOXD"],
|
||||
[176, "DIMCLRD"],
|
||||
[177, "DIMCLRE"],
|
||||
[178, "DIMCLRT"],
|
||||
[271, "DIMDEC"],
|
||||
[278 ,"DIMDSEP"],
|
||||
[281, "DIMSD1"],
|
||||
[282, "DIMSD2"],
|
||||
[3, "DIMPOST"],
|
||||
[40, "DIMSCALE"],
|
||||
[41, "DIMASZ"],
|
||||
[42, "DIMEXO"],
|
||||
[44, "DIMEXE"],
|
||||
[45, "DIMRND"],
|
||||
[46, "DIMDLE"],
|
||||
[5, "DIMBLK"],
|
||||
[6, "DIMBLK1"],
|
||||
[7, "DIMBLK2"],
|
||||
[75, "DIMSE1"],
|
||||
[76, "DIMSE2"],
|
||||
[78, "DIMZIN"],
|
||||
])
|
||||
|
||||
export default codes
|
||||
@@ -0,0 +1,168 @@
|
||||
import log from '@/utils/log/Logger';
|
||||
|
||||
export interface IGroup {
|
||||
code: number;
|
||||
value: number | string | boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* DxfArrayScanner
|
||||
*
|
||||
* 基于AutoCAD 2012 DXF参考
|
||||
* http://images.autodesk.com/adsk/files/autocad_2012_pdf_dxf-reference_enu.pdf
|
||||
*
|
||||
* 读取表示dxf文件行的数组。接受一个数组,并提供一个简单的接口来提取组代码和值对。
|
||||
* @param data - 一个数组,其中每个元素表示DXF文件中的一行
|
||||
* @constructor
|
||||
*/
|
||||
export default class DxfArrayScanner {
|
||||
private _pointer = 0;
|
||||
private _eof = false;
|
||||
public lastReadGroup: IGroup | undefined;
|
||||
private _data: string[];
|
||||
|
||||
constructor(data: string[]) {
|
||||
this._data = data;
|
||||
// console.log('this._data:', this._data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数组中获取下一组(代码、值)。组是数组中两个连续的元素。第一个是代码,第二个是值。
|
||||
* @returns {{code: Number}|*}
|
||||
*/
|
||||
public next() {
|
||||
if (!this.hasNext()) {
|
||||
// console.log('this._eof:', this._eof);
|
||||
// console.log('this._pointer:', this._pointer);
|
||||
|
||||
if (!this._eof){
|
||||
log.error('输入结束异常:EOF组未在文件结束前读取。代码结束: ' + this._data[this._pointer]);
|
||||
}else{
|
||||
log.error('在读取完 EOF 组后无法调用 `next`');
|
||||
}
|
||||
|
||||
this._eof = true;
|
||||
return { code: 0, value: 'EOF' };
|
||||
}
|
||||
|
||||
const group = {
|
||||
code: parseInt(this._data[this._pointer])
|
||||
} as IGroup;
|
||||
|
||||
if(isNaN(group.code)){
|
||||
// 回退code: 如果当前code为NaN,很可能是因为当前行和上一行是连贯数据,被libreDWG解析为两行,所以要回退code采用同一个code解析
|
||||
this._pointer--;
|
||||
group.code = parseInt(this._data[this._pointer - 1]);
|
||||
|
||||
// TODO: 或者使用跳过当前行操作?
|
||||
// this._pointer++;
|
||||
// return this.next();
|
||||
}
|
||||
|
||||
this._pointer++;
|
||||
|
||||
group.value = parseGroupValue(group.code, this._data[this._pointer].trim());
|
||||
|
||||
this._pointer++;
|
||||
|
||||
// if (group.code === 0 && (group.value === 'EOF' || (group.value === 'ENDSEC' && !this.hasNext()))) this._eof = true;
|
||||
if (group.code === 0 && group.value === 'EOF') this._eof = true;
|
||||
|
||||
this.lastReadGroup = group;
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
public peek() {
|
||||
if (!this.hasNext()) {
|
||||
if (!this._eof)
|
||||
throw new Error('输入结束异常:EOF组未在文件结束前读取。代码结束: ' + this._data[this._pointer]);
|
||||
else
|
||||
throw new Error('Cannot call \'next\' after EOF group has been read');
|
||||
}
|
||||
|
||||
const group = {
|
||||
code: parseInt(this._data[this._pointer])
|
||||
} as IGroup;
|
||||
|
||||
group.value = parseGroupValue(group.code, this._data[this._pointer + 1].trim());
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
public rewind(numberOfGroups = 1) {
|
||||
this._pointer = this._pointer - numberOfGroups * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果存在另一个代码/值对(数组中的2个元素)则返回true。
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public hasNext() {
|
||||
// 检查我们是否读过EOF组代码
|
||||
if (this._eof) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 我们需要确保有两条线路可用
|
||||
return this._pointer <= this._data.length - 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果扫描器位于数组的末尾,则返回true
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isEOF() {
|
||||
return this._eof;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将值解析为其适当类型。
|
||||
* 参见本文件顶部提供的AutoCAD DXF 2012参考的第3-10页。
|
||||
*
|
||||
* @param code
|
||||
* @param value
|
||||
* @returns {*}
|
||||
*/
|
||||
function parseGroupValue(code: number, value: string) {
|
||||
if (code <= 9) return value;
|
||||
if (code >= 10 && code <= 59) return parseFloat(value);
|
||||
if (code >= 60 && code <= 99) return parseInt(value);
|
||||
if (code >= 100 && code <= 109) return value;
|
||||
if (code >= 110 && code <= 149) return parseFloat(value);
|
||||
if (code >= 160 && code <= 179) return parseInt(value);
|
||||
if (code >= 210 && code <= 239) return parseFloat(value);
|
||||
if (code >= 270 && code <= 289) return parseInt(value);
|
||||
if (code >= 290 && code <= 299) return parseBoolean(value as '0' | '1');
|
||||
if (code >= 300 && code <= 369) return value;
|
||||
if (code >= 370 && code <= 389) return parseInt(value);
|
||||
if (code >= 390 && code <= 399) return value;
|
||||
if (code >= 400 && code <= 409) return parseInt(value);
|
||||
if (code >= 410 && code <= 419) return value;
|
||||
if (code >= 420 && code <= 429) return parseInt(value);
|
||||
if (code >= 430 && code <= 439) return value;
|
||||
if (code >= 440 && code <= 459) return parseInt(value);
|
||||
if (code >= 460 && code <= 469) return parseFloat(value);
|
||||
if (code >= 470 && code <= 481) return value;
|
||||
if (code === 999) return value;
|
||||
if (code >= 1000 && code <= 1009) return value;
|
||||
if (code >= 1010 && code <= 1059) return parseFloat(value);
|
||||
if (code >= 1060 && code <= 1071) return parseInt(value);
|
||||
|
||||
//log.warn('group代码没有定义的类型: %j', { code: code, value: value });
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据1或0值解析布尔值
|
||||
* @param str
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function parseBoolean(str: '0' | '1') {
|
||||
if (str === '0') return false;
|
||||
if (str === '1') return true;
|
||||
return str;
|
||||
// throw TypeError('String \'' + str + '\' cannot be cast to Boolean type');
|
||||
}
|
||||
@@ -0,0 +1,980 @@
|
||||
import {Readable} from 'stream';
|
||||
import DxfArrayScanner, {IGroup} from './DxfArrayScanner';
|
||||
import AUTO_CAD_COLOR_INDEX from './AutoCadColorIndex';
|
||||
import dimStyleCodes from './DimStyleCodes';
|
||||
|
||||
import Face from './entities/3dface';
|
||||
import Arc from './entities/arc';
|
||||
import AttDef from './entities/attdef';
|
||||
import Attribute from './entities/attribute'
|
||||
import Circle from './entities/circle';
|
||||
import Dimension from './entities/dimension';
|
||||
import Ellipse from './entities/ellipse';
|
||||
import Hatch from './entities/hatch';
|
||||
import Insert from './entities/insert';
|
||||
import Line from './entities/line';
|
||||
import LWPolyline from './entities/lwpolyline';
|
||||
import MText from './entities/mtext';
|
||||
import Point from './entities/point';
|
||||
import Polyline from './entities/polyline';
|
||||
import Solid from './entities/solid';
|
||||
import Spline from './entities/spline';
|
||||
import Text from './entities/text';
|
||||
import Vertex from './entities/vertex';
|
||||
|
||||
import log from '@/utils/log/Logger';
|
||||
import IGeometry, {EntityName, IEntity, IPoint} from './entities/geomtry';
|
||||
|
||||
export interface IBlock {
|
||||
entities: IEntity[];
|
||||
type: number;
|
||||
ownerHandle: string;
|
||||
// entities: any[];
|
||||
xrefPath: string;
|
||||
name: string;
|
||||
name2: string;
|
||||
handle: string;
|
||||
layer: string;
|
||||
position: IPoint;
|
||||
paperSpace: boolean;
|
||||
}
|
||||
|
||||
export interface IViewPort {
|
||||
name: string;
|
||||
lowerLeftCorner: IPoint;
|
||||
upperRightCorner: IPoint;
|
||||
center: IPoint;
|
||||
snapBasePoint: IPoint;
|
||||
snapSpacing: IPoint;
|
||||
gridSpacing: IPoint;
|
||||
viewDirectionFromTarget: IPoint;
|
||||
viewTarget: IPoint;
|
||||
lensLength: number;
|
||||
frontClippingPlane: string | number | boolean;
|
||||
backClippingPlane: string | number | boolean;
|
||||
viewHeight: number;
|
||||
snapRotationAngle: number;
|
||||
viewTwistAngle: number;
|
||||
orthographicType: string;
|
||||
ucsOrigin: IPoint;
|
||||
ucsXAxis: IPoint;
|
||||
ucsYAxis: IPoint;
|
||||
renderMode: string;
|
||||
defaultLightingType: string;
|
||||
defaultLightingOn: string;
|
||||
ownerHandle: string;
|
||||
ambientColor: number;
|
||||
viewMode: string;
|
||||
}
|
||||
|
||||
export interface IViewPortTableDefinition {
|
||||
tableRecordsProperty: 'viewPorts';
|
||||
tableName: 'viewPort';
|
||||
dxfSymbolName: 'VPORT';
|
||||
|
||||
parseTableRecords(): IViewPort[];
|
||||
}
|
||||
|
||||
export interface ILineType {
|
||||
name: string;
|
||||
description: string;
|
||||
pattern: string[];
|
||||
patternLength: number;
|
||||
}
|
||||
|
||||
export interface ILineTypeTableDefinition {
|
||||
tableRecordsProperty: 'lineTypes';
|
||||
tableName: 'lineType';
|
||||
dxfSymbolName: 'LTYPE';
|
||||
|
||||
parseTableRecords(): Record<string, ILineType>;
|
||||
}
|
||||
|
||||
export interface ILayer {
|
||||
name: string;
|
||||
visible: boolean;
|
||||
colorIndex: number;
|
||||
color: number;
|
||||
frozen: boolean;
|
||||
}
|
||||
|
||||
export interface ILayerTableDefinition {
|
||||
tableRecordsProperty: 'layers';
|
||||
tableName: 'layer';
|
||||
dxfSymbolName: 'LAYER';
|
||||
|
||||
parseTableRecords(): Record<string, ILayer>;
|
||||
}
|
||||
|
||||
export interface ITableDefinitions {
|
||||
VPORT: IViewPortTableDefinition;
|
||||
LTYPE: ILineTypeTableDefinition;
|
||||
LAYER: ILayerTableDefinition;
|
||||
DIMSTYLE: any;
|
||||
STYLE: any;
|
||||
}
|
||||
|
||||
export interface IBaseTable {
|
||||
handle: string;
|
||||
ownerHandle: string;
|
||||
}
|
||||
|
||||
export interface IViewPortTable extends IBaseTable {
|
||||
viewPorts: IViewPort[];
|
||||
}
|
||||
|
||||
export interface ILayerTypesTable extends IBaseTable {
|
||||
lineTypes: Record<string, ILineType>;
|
||||
}
|
||||
|
||||
export interface ILayersTable extends IBaseTable {
|
||||
layers: Record<string, ILayer>;
|
||||
}
|
||||
|
||||
export interface ITables {
|
||||
viewPort: IViewPortTable;
|
||||
lineType: ILayerTypesTable;
|
||||
layer: ILayersTable;
|
||||
}
|
||||
|
||||
export type ITable = IViewPortTable | ILayerTypesTable | ILayersTable;
|
||||
|
||||
export interface IDxf {
|
||||
header: Record<string, IPoint | number>;
|
||||
entities: IEntity[];
|
||||
blocks: Record<string, IBlock>;
|
||||
tables: ITables;
|
||||
}
|
||||
|
||||
function registerDefaultEntityHandlers(dxfParser: DxfParser) {
|
||||
// 这里支持的实体(一些实体代码仍然被重构到这个流中)
|
||||
dxfParser.registerEntityHandler(Face);
|
||||
dxfParser.registerEntityHandler(Arc);
|
||||
dxfParser.registerEntityHandler(AttDef);
|
||||
dxfParser.registerEntityHandler(Attribute);
|
||||
dxfParser.registerEntityHandler(Circle);
|
||||
dxfParser.registerEntityHandler(Dimension);
|
||||
dxfParser.registerEntityHandler(Ellipse);
|
||||
dxfParser.registerEntityHandler(Hatch);
|
||||
dxfParser.registerEntityHandler(Insert);
|
||||
dxfParser.registerEntityHandler(Line);
|
||||
dxfParser.registerEntityHandler(LWPolyline);
|
||||
dxfParser.registerEntityHandler(MText);
|
||||
dxfParser.registerEntityHandler(Point);
|
||||
dxfParser.registerEntityHandler(Polyline);
|
||||
dxfParser.registerEntityHandler(Solid);
|
||||
dxfParser.registerEntityHandler(Spline);
|
||||
dxfParser.registerEntityHandler(Text);
|
||||
dxfParser.registerEntityHandler(Vertex);
|
||||
}
|
||||
|
||||
export default class DxfParser {
|
||||
private _entityHandlers = {} as Record<EntityName, IGeometry>;
|
||||
|
||||
constructor() {
|
||||
registerDefaultEntityHandlers(this);
|
||||
}
|
||||
|
||||
public parse(source: string | Readable) {
|
||||
if (typeof source === 'string') {
|
||||
return this._parse(source);
|
||||
} else {
|
||||
log.error('无法读取dxf源的类型: `' + typeof (source));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public registerEntityHandler(handlerType: new () => IGeometry) {
|
||||
const instance = new handlerType();
|
||||
this._entityHandlers[instance.ForEntityName] = instance;
|
||||
}
|
||||
|
||||
public parseSync(source: string) {
|
||||
return this.parse(source);
|
||||
}
|
||||
|
||||
public parseStream(stream: Readable) {
|
||||
let dxfString = "";
|
||||
const self = this;
|
||||
return new Promise<IDxf>((res, rej) => {
|
||||
|
||||
stream.on('data', (chunk) => {
|
||||
dxfString += chunk;
|
||||
});
|
||||
stream.on('end', () => {
|
||||
try {
|
||||
res(self._parse(dxfString));
|
||||
} catch (err) {
|
||||
rej(err);
|
||||
}
|
||||
});
|
||||
stream.on('error', (err) => {
|
||||
rej(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _parse(dxfString: string) {
|
||||
const dxf = {} as IDxf;
|
||||
let lastHandle = 0;
|
||||
const dxfLinesArray = dxfString.split(/\r\n|\r|\n/g);
|
||||
|
||||
const scanner = new DxfArrayScanner(dxfLinesArray);
|
||||
if (!scanner.hasNext()) {
|
||||
log.error('当前 CAD 文件为空文件');
|
||||
return dxf;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
let curr: IGroup;
|
||||
|
||||
function parseAll() {
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0 && curr.value === 'SECTION') {
|
||||
curr = scanner.next();
|
||||
|
||||
// 确保我们读的是段代码
|
||||
if (curr.code !== 2) {
|
||||
log.error(`Unexpected code ${debugCode(curr) } after 0:SECTION`);
|
||||
curr = scanner.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curr.value === 'HEADER') {
|
||||
dxf.header = parseHeader();
|
||||
} else if (curr.value === 'BLOCKS') {
|
||||
dxf.blocks = parseBlocks();
|
||||
} else if (curr.value === 'ENTITIES') {
|
||||
dxf.entities = parseEntities(false);
|
||||
} else if (curr.value === 'TABLES') {
|
||||
dxf.tables = parseTables();
|
||||
} else if (curr.value === 'EOF') {
|
||||
log.debug('EOF');
|
||||
} else {
|
||||
log.warn('Skipping section ' + curr.value);
|
||||
}
|
||||
} else {
|
||||
curr = scanner.next();
|
||||
}
|
||||
// If is a new section
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {object} header
|
||||
*/
|
||||
function parseHeader() {
|
||||
// interesting variables:
|
||||
// $ACADVER, $VIEWDIR, $VIEWSIZE, $VIEWCTR, $TDCREATE, $TDUPDATE
|
||||
// http://www.autodesk.com/techpubs/autocad/acadr14/dxf/header_section_al_u05_c.htm
|
||||
// Also see VPORT table entries
|
||||
let currVarName = null as null | string;
|
||||
let currVarValue = null as null | IPoint | number;
|
||||
const header = {} as Record<string, IPoint | number>;
|
||||
// loop through header variables
|
||||
curr = scanner.next();
|
||||
|
||||
while (true) {
|
||||
if (groupIs(curr, 0, 'ENDSEC')) {
|
||||
if (currVarName) header[currVarName as string] = currVarValue!;
|
||||
break;
|
||||
} else if (curr.code === 9) {
|
||||
if (currVarName) header[currVarName as string] = currVarValue!;
|
||||
currVarName = curr.value as string;
|
||||
// Filter here for particular variables we are interested in
|
||||
} else {
|
||||
if (curr.code === 10) {
|
||||
currVarValue = {x: curr.value as number} as IPoint;
|
||||
} else if (curr.code === 20) {
|
||||
(currVarValue as IPoint).y = curr.value as number;
|
||||
} else if (curr.code === 30) {
|
||||
(currVarValue as IPoint).z = curr.value as number;
|
||||
} else {
|
||||
currVarValue = curr.value as number;
|
||||
}
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
// console.log(util.inspect(header, { colors: true, depth: null }));
|
||||
curr = scanner.next(); // swallow up ENDSEC
|
||||
return header;
|
||||
}
|
||||
|
||||
function parseBlocks() {
|
||||
const blocks = {} as Record<string, IBlock>;
|
||||
|
||||
curr = scanner.next();
|
||||
|
||||
while (curr.value !== 'EOF') {
|
||||
if (groupIs(curr, 0, 'ENDSEC')) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (groupIs(curr, 0, 'BLOCK')) {
|
||||
const block = parseBlock();
|
||||
ensureHandle(block);
|
||||
if (!block.name) {
|
||||
// log.error('block with handle "' + block.handle + '" is missing a name.');
|
||||
// block.name = 'InvalidBlockName-' + block.ownerHandle;
|
||||
}else{
|
||||
blocks[block.name] = block;
|
||||
}
|
||||
} else {
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function parseBlock() {
|
||||
const block = {} as IBlock;
|
||||
curr = scanner.next();
|
||||
|
||||
while (curr.value !== 'EOF') {
|
||||
switch (curr.code) {
|
||||
case 1:
|
||||
block.xrefPath = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 2:
|
||||
block.name = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 3:
|
||||
block.name2 = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 5:
|
||||
block.handle = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 8:
|
||||
block.layer = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 10:
|
||||
block.position = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 67:
|
||||
block.paperSpace = (curr.value && curr.value == 1) ? true : false;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 70:
|
||||
if (curr.value != 0) {
|
||||
//if(curr.value & BLOCK_ANONYMOUS_FLAG) console.log(' Anonymous block');
|
||||
//if(curr.value & BLOCK_NON_CONSTANT_FLAG) console.log(' Non-constant attributes');
|
||||
//if(curr.value & BLOCK_XREF_FLAG) console.log(' Is xref');
|
||||
//if(curr.value & BLOCK_XREF_OVERLAY_FLAG) console.log(' Is xref overlay');
|
||||
//if(curr.value & BLOCK_EXTERNALLY_DEPENDENT_FLAG) console.log(' Is externally dependent');
|
||||
//if(curr.value & BLOCK_RESOLVED_OR_DEPENDENT_FLAG) console.log(' Is resolved xref or dependent of an xref');
|
||||
//if(curr.value & BLOCK_REFERENCED_XREF) console.log(' This definition is a referenced xref');
|
||||
block.type = curr.value as number;
|
||||
}
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 100:
|
||||
// ignore class markers
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 330:
|
||||
block.ownerHandle = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 0:
|
||||
if (curr.value == 'ENDBLK')
|
||||
break;
|
||||
block.entities = parseEntities(true);
|
||||
break;
|
||||
default:
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
if (groupIs(curr, 0, 'ENDBLK')) {
|
||||
curr = scanner.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* parseTables
|
||||
* @return {Object} Object representing tables
|
||||
*/
|
||||
function parseTables() {
|
||||
const tables = {} as ITables;
|
||||
curr = scanner.next();
|
||||
while (curr.value !== 'EOF') {
|
||||
if (groupIs(curr, 0, 'ENDSEC'))
|
||||
break;
|
||||
|
||||
if (groupIs(curr, 0, 'TABLE')) {
|
||||
curr = scanner.next();
|
||||
|
||||
const tableDefinition = tableDefinitions[curr.value as keyof ITableDefinitions];
|
||||
if (tableDefinition) {
|
||||
tables[tableDefinitions[curr.value as keyof ITableDefinitions].tableName] = parseTable(curr);
|
||||
} else {
|
||||
// log.debug('Unhandled Table ' + curr.value);
|
||||
}
|
||||
} else {
|
||||
// else ignored
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
|
||||
curr = scanner.next();
|
||||
return tables;
|
||||
}
|
||||
|
||||
const END_OF_TABLE_VALUE = 'ENDTAB';
|
||||
|
||||
function parseTable<T extends IBaseTable = ITable>(group: IGroup) {
|
||||
const tableDefinition = tableDefinitions[group.value as keyof ITableDefinitions];
|
||||
const table = {} as T;
|
||||
let expectedCount = 0;
|
||||
|
||||
curr = scanner.next();
|
||||
while (!groupIs(curr, 0, END_OF_TABLE_VALUE)) {
|
||||
switch (curr.code) {
|
||||
case 5:
|
||||
table.handle = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 330:
|
||||
table.ownerHandle = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 100:
|
||||
if (curr.value === 'AcDbSymbolTable') {
|
||||
// ignore
|
||||
curr = scanner.next();
|
||||
} else {
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
}
|
||||
break;
|
||||
case 70:
|
||||
expectedCount = curr.value as number;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 0:
|
||||
if (curr.value === tableDefinition.dxfSymbolName) {
|
||||
table[tableDefinition.tableRecordsProperty] = tableDefinition.parseTableRecords();
|
||||
} else {
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
const tableRecords = table[tableDefinition.tableRecordsProperty];
|
||||
if (tableRecords) {
|
||||
let actualCount = (() => {
|
||||
if (tableRecords.constructor === Array) {
|
||||
return tableRecords.length;
|
||||
} else if (typeof (tableRecords) === 'object') {
|
||||
return Object.keys(tableRecords).length;
|
||||
}
|
||||
return undefined;
|
||||
})();
|
||||
if (expectedCount !== actualCount) log.warn('Parsed ' + actualCount + ' ' + tableDefinition.dxfSymbolName + '\'s but expected ' + expectedCount);
|
||||
}
|
||||
curr = scanner.next();
|
||||
return table;
|
||||
}
|
||||
|
||||
function parseViewPortRecords() {
|
||||
const viewPorts = [] as IViewPort[]; // 多个表项可以具有相同的名称,表示多个视区配置
|
||||
let viewPort = {} as IViewPort;
|
||||
|
||||
curr = scanner.next();
|
||||
while (!groupIs(curr, 0, END_OF_TABLE_VALUE)) {
|
||||
|
||||
switch (curr.code) {
|
||||
case 2: // 视口名
|
||||
viewPort.name = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 10: // 视口的左下角
|
||||
viewPort.lowerLeftCorner = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 11: // 视口右上角
|
||||
viewPort.upperRightCorner = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 12: // 视口中心点(在 DCS 中)
|
||||
viewPort.center = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 13: // 捕捉基点(在 DCS 中)
|
||||
viewPort.snapBasePoint = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 14: // 捕捉间距 X 和 Y
|
||||
viewPort.snapSpacing = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 15: // 栅格间距 X 和 Y
|
||||
viewPort.gridSpacing = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 16: // 相对于目标点的观察方向(在 WCS 中)
|
||||
viewPort.viewDirectionFromTarget = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 17: //观察目标点(在 WCS 中)
|
||||
viewPort.viewTarget = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 42: // 焦距
|
||||
viewPort.lensLength = curr.value as number;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 43: //前向剪裁平面(距目标点的偏移)
|
||||
viewPort.frontClippingPlane = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 44: // 后向剪裁平面(距目标点的偏移)
|
||||
viewPort.backClippingPlane = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 45: // 视图高度
|
||||
viewPort.viewHeight = curr.value as number;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 50: // 捕捉旋转角度
|
||||
viewPort.snapRotationAngle = curr.value as number;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 51: // 视图扭转角度
|
||||
viewPort.viewTwistAngle = curr.value as number;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 71: // 视图模式(参见 VIEWMODE 系统变量)
|
||||
viewPort.viewMode = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 79: // UCS 的正交类型 0 = UCS 不正交 1 = 俯视图;2 = 仰视图 3 = 主视图;4 = 后视图 5 = 左视图;6 = 右视图
|
||||
viewPort.orthographicType = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 110: // UCS 原点(在 DCS 中)
|
||||
viewPort.ucsOrigin = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 111: // UCS X 轴
|
||||
viewPort.ucsXAxis = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 112: // UCS Y 轴
|
||||
viewPort.ucsYAxis = parsePoint(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 281:
|
||||
/*
|
||||
* 渲染模式: 0 = 二维优化(传统二维) 1 = 线框图 2 = 隐藏线 3 = 平面着色 4 = 体着色 5 = 带线框平面着色 6 = 带线框体着色
|
||||
* 所有非二维优化渲染模式均使用新三维图形管道。这些值直接与 SHADEMODE 命令和 AcDbAbstractViewTableRecord::RenderMode 枚举相对应
|
||||
*/
|
||||
viewPort.renderMode = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 292:
|
||||
viewPort.defaultLightingOn = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 330:
|
||||
viewPort.ownerHandle = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 63: // These are all ambient color. Perhaps should be a gradient when multiple are set.
|
||||
case 421:
|
||||
case 431:
|
||||
viewPort.ambientColor = curr.value as number;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 0:
|
||||
// New ViewPort
|
||||
if (curr.value === 'VPORT') {
|
||||
viewPorts.push(viewPort);
|
||||
viewPort = {} as IViewPort;
|
||||
curr = scanner.next();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Note: do not call scanner.next() here,
|
||||
// parseTable() needs the current group
|
||||
viewPorts.push(viewPort);
|
||||
|
||||
return viewPorts;
|
||||
}
|
||||
|
||||
function parseLineTypes() {
|
||||
const ltypes = {} as Record<string, ILineType>;
|
||||
let ltype = {} as ILineType;
|
||||
let length = 0;
|
||||
let ltypeName: string;
|
||||
|
||||
curr = scanner.next();
|
||||
while (!groupIs(curr, 0, 'ENDTAB')) {
|
||||
|
||||
switch (curr.code) {
|
||||
case 2:
|
||||
ltype.name = curr.value as string;
|
||||
ltypeName = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 3:
|
||||
ltype.description = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 73: // Number of elements for this line type (dots, dashes, spaces);
|
||||
length = curr.value as number;
|
||||
if (length > 0) ltype.pattern = [];
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 40: // total pattern length
|
||||
ltype.patternLength = curr.value as number;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 49:
|
||||
ltype.pattern.push(curr.value as string);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 0:
|
||||
if (length > 0 && length !== ltype.pattern.length) log.warn('lengths do not match on LTYPE pattern');
|
||||
ltypes[ltypeName!] = ltype;
|
||||
ltype = {} as ILineType;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
default:
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
|
||||
ltypes[ltypeName!] = ltype;
|
||||
return ltypes;
|
||||
}
|
||||
|
||||
function parseLayers() {
|
||||
const layers = {} as Record<string, ILayer>;
|
||||
let layer = {} as ILayer;
|
||||
let layerName: string | undefined;
|
||||
|
||||
curr = scanner.next();
|
||||
while (!groupIs(curr, 0, 'ENDTAB')) {
|
||||
|
||||
switch (curr.code) {
|
||||
case 2: // layer name
|
||||
layer.name = curr.value as string;
|
||||
layerName = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 62: // color, visibility
|
||||
// @ts-ignore
|
||||
layer.visible = curr.value >= 0;
|
||||
// TODO 0 and 256 are BYBLOCK and BYLAYER respectively. Need to handle these values for layers?.
|
||||
layer.colorIndex = Math.abs(curr.value as number);
|
||||
layer.color = getAcadColor(layer.colorIndex as number);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 70: // frozen layer
|
||||
layer.frozen = (((curr.value as number) & 1) != 0 || ((curr.value as number) & 2) != 0);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 0:
|
||||
// New Layer
|
||||
if (curr.value === 'LAYER') {
|
||||
layers[layerName!] = layer;
|
||||
layer = {} as ILayer;
|
||||
layerName = undefined;
|
||||
curr = scanner.next();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Note: do not call scanner.next() here,
|
||||
// parseLayerTable() needs the current group
|
||||
layers[layerName!] = layer;
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
function parseDimStyles() {
|
||||
let dimStyles = {},
|
||||
styleName: string | undefined,
|
||||
style: any = {};
|
||||
|
||||
curr = scanner.next();
|
||||
while (!groupIs(curr, 0, 'ENDTAB')) {
|
||||
if (dimStyleCodes.has(curr.code)) {
|
||||
style[dimStyleCodes.get(curr.code) as string] = curr.value
|
||||
curr = scanner.next();
|
||||
} else {
|
||||
switch (curr.code) {
|
||||
case 2: // style name
|
||||
style.name = curr.value;
|
||||
styleName = curr.value as string;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 0:
|
||||
// New style
|
||||
if (curr.value === 'DIMSTYLE') {
|
||||
// @ts-ignore
|
||||
dimStyles[styleName] = style;
|
||||
style = {};
|
||||
styleName = undefined;
|
||||
curr = scanner.next();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note: do not call scanner.next() here,
|
||||
// parseLayerTable() needs the current group
|
||||
dimStyles[styleName as string] = style;
|
||||
|
||||
return dimStyles;
|
||||
}
|
||||
|
||||
const parseStyles = function () {
|
||||
const styles = {};
|
||||
let style: any = {};
|
||||
let styleName;
|
||||
|
||||
curr = scanner.next();
|
||||
while (!groupIs(curr, 0, END_OF_TABLE_VALUE)) {
|
||||
switch (curr.code) {
|
||||
case 100:
|
||||
style.subClassMarker = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 2:
|
||||
style.styleName = curr.value;
|
||||
styleName = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 70:
|
||||
style.standardFlag = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 40:
|
||||
style.fixedTextHeight = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 41:
|
||||
style.widthFactor = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 50:
|
||||
style.obliqueAngle = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 71:
|
||||
style.textGenerationFlag = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 42:
|
||||
style.lastHeight = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 3:
|
||||
style.font = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 4:
|
||||
style.bigFont = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 1071:
|
||||
style.extendedFont = curr.value;
|
||||
curr = scanner.next();
|
||||
break;
|
||||
case 0:
|
||||
if (curr.value === 'STYLE') {
|
||||
styles[styleName] = style;
|
||||
style = {};
|
||||
styleName = undefined;
|
||||
curr = scanner.next();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logUnhandledGroup(curr);
|
||||
curr = scanner.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
styles[styleName] = style;
|
||||
return styles;
|
||||
};
|
||||
|
||||
const tableDefinitions = {
|
||||
VPORT: {
|
||||
tableRecordsProperty: 'viewPorts',
|
||||
tableName: 'viewPort',
|
||||
dxfSymbolName: 'VPORT',
|
||||
parseTableRecords: parseViewPortRecords
|
||||
},
|
||||
LTYPE: {
|
||||
tableRecordsProperty: 'lineTypes',
|
||||
tableName: 'lineType',
|
||||
dxfSymbolName: 'LTYPE',
|
||||
parseTableRecords: parseLineTypes
|
||||
},
|
||||
LAYER: {
|
||||
tableRecordsProperty: 'layers',
|
||||
tableName: 'layer',
|
||||
dxfSymbolName: 'LAYER',
|
||||
parseTableRecords: parseLayers
|
||||
},
|
||||
DIMSTYLE: {
|
||||
tableRecordsProperty: 'dimStyles',
|
||||
tableName: 'dimstyle',
|
||||
dxfSymbolName: 'DIMSTYLE',
|
||||
parseTableRecords: parseDimStyles
|
||||
},
|
||||
STYLE: {
|
||||
tableRecordsProperty: 'styles',
|
||||
tableName: 'style',
|
||||
dxfSymbolName: 'STYLE',
|
||||
parseTableRecords: parseStyles,
|
||||
},
|
||||
} as ITableDefinitions;
|
||||
|
||||
/**
|
||||
* 在解析器首次读取0:ENTITIES组后调用。扫描器应该已经在第一个实体的开始。
|
||||
* @return {Array} the resulting entities
|
||||
*/
|
||||
function parseEntities(forBlock: boolean) {
|
||||
const entities = [] as IEntity[];
|
||||
|
||||
const endingOnValue = forBlock ? 'ENDBLK' : 'ENDSEC';
|
||||
|
||||
if (!forBlock) {
|
||||
curr = scanner.next();
|
||||
}
|
||||
while (true) {
|
||||
if (curr.code === 0) {
|
||||
if (curr.value === endingOnValue) {
|
||||
break;
|
||||
}
|
||||
|
||||
const handler = self._entityHandlers[curr.value as EntityName];
|
||||
if (handler != null) {
|
||||
const entity = handler.parseEntity(scanner, curr);
|
||||
curr = scanner.lastReadGroup!;
|
||||
ensureHandle(entity);
|
||||
entities.push(entity);
|
||||
} else {
|
||||
// log.debug('Unhandled entity ' + curr.value);
|
||||
curr = scanner.next();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// ignored lines from unsupported entity
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
if (endingOnValue == 'ENDSEC') curr = scanner.next(); // swallow up ENDSEC, but not ENDBLK
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析一个2D或3D点.
|
||||
* 如果它是3D的,将其作为具有x, y和(有时)z属性的对象返回。
|
||||
* 假定当前组是被读入点的x,并且scanner.next()将返回y。
|
||||
* 解析器将自动确定是否存在z点。
|
||||
* @return {Object} 二维或三维点作为具有 x、y[,z] 的对象
|
||||
*/
|
||||
function parsePoint(curr: IGroup) {
|
||||
const point = {} as IPoint;
|
||||
let code = curr.code;
|
||||
|
||||
point.x = curr.value as number;
|
||||
|
||||
code += 10;
|
||||
curr = scanner.next();
|
||||
if (curr.code != code)
|
||||
throw new Error('Expected code for point value to be ' + code +
|
||||
' but got ' + curr.code + '.');
|
||||
point.y = curr.value as number;
|
||||
|
||||
code += 10;
|
||||
curr = scanner.next();
|
||||
if (curr.code != code) {
|
||||
scanner.rewind();
|
||||
return point;
|
||||
}
|
||||
point.z = curr.value as number;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
function ensureHandle(entity: IEntity | IBlock) {
|
||||
if (!entity) throw new TypeError('entity cannot be undefined or null');
|
||||
|
||||
if (!entity.handle) entity.handle = lastHandle++;
|
||||
}
|
||||
|
||||
parseAll();
|
||||
return dxf;
|
||||
}
|
||||
}
|
||||
|
||||
function groupIs(group: IGroup, code: number, value: string | number | boolean) {
|
||||
return group.code === code && group.value === value;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
function logUnhandledGroup(curr: IGroup) {
|
||||
// log.debug('unhandled group ' + debugCode(curr));
|
||||
}
|
||||
|
||||
|
||||
function debugCode(curr: IGroup) {
|
||||
return curr.code + ':' + curr.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the truecolor value of the given AutoCad color index value
|
||||
* @return {Number} truecolor value as a number
|
||||
*/
|
||||
function getAcadColor(index: number) {
|
||||
return AUTO_CAD_COLOR_INDEX[index];
|
||||
}
|
||||
|
||||
// const BLOCK_ANONYMOUS_FLAG = 1;
|
||||
// const BLOCK_NON_CONSTANT_FLAG = 2;
|
||||
// const BLOCK_XREF_FLAG = 4;
|
||||
// const BLOCK_XREF_OVERLAY_FLAG = 8;
|
||||
// const BLOCK_EXTERNALLY_DEPENDENT_FLAG = 16;
|
||||
// const BLOCK_RESOLVED_OR_DEPENDENT_FLAG = 32;
|
||||
// const BLOCK_REFERENCED_XREF = 64;
|
||||
|
||||
|
||||
/* Notes */
|
||||
// Code 6 of an entity indicates inheritance of properties (eg. color).
|
||||
// BYBLOCK means inherits from block
|
||||
// BYLAYER (default) mean inherits from layer
|
||||
@@ -0,0 +1,121 @@
|
||||
import log from '@/utils/log/Logger';
|
||||
|
||||
export default class ExtendedDataParser {
|
||||
private appName: null;
|
||||
private appNameWarningShown: boolean;
|
||||
private lastString: any;
|
||||
private sectionStack: { values: any[] }[];
|
||||
private failure: boolean;
|
||||
|
||||
constructor() {
|
||||
this.appName = null
|
||||
this.appNameWarningShown = false
|
||||
this.lastString = null
|
||||
this.sectionStack = [this._CreateSection()]
|
||||
this.failure = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed next token.
|
||||
* @return {boolean} 如果应该为此令牌创建新的解析器实例,则为True。
|
||||
*/
|
||||
Feed(curr) {
|
||||
if (!this.appName) {
|
||||
if (curr.code == 1001) {
|
||||
this.appName = curr.value
|
||||
return false
|
||||
}
|
||||
if (!this.appNameWarningShown) {
|
||||
this.appNameWarningShown = true
|
||||
log.warn("XDATA部分不以应用程序名称开头")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (curr.code == 1001) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (this.failure) {
|
||||
/* Ignore all the rest content in case of parsing failure. */
|
||||
return false
|
||||
}
|
||||
|
||||
if (curr.code == 1000) {
|
||||
if (this.lastString) {
|
||||
log.warn("XDATA section unused string: " + this.lastString)
|
||||
}
|
||||
this.lastString = curr.value
|
||||
return false
|
||||
}
|
||||
|
||||
const curSection = this._currentSection
|
||||
|
||||
if (curr.code == 1002) {
|
||||
if (curr.value == "{") {
|
||||
if (!this.lastString) {
|
||||
log.warn("Unnamed XDATA section encountered")
|
||||
this.failure = true
|
||||
return false
|
||||
}
|
||||
const newSection = this._CreateSection()
|
||||
curSection[this.lastString] = newSection
|
||||
this.lastString = null
|
||||
this.sectionStack.push(newSection)
|
||||
return false
|
||||
}
|
||||
if (curr.value == "}") {
|
||||
if (this.sectionStack.length < 2) {
|
||||
log.warn("Unmatched XDATA section closing")
|
||||
this.failure = true
|
||||
return false
|
||||
}
|
||||
this.sectionStack.length = this.sectionStack.length - 1
|
||||
return false
|
||||
}
|
||||
log.warn("Bad XDATA section control string encountered: " + curr.value)
|
||||
this.failure = true
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.lastString !== null) {
|
||||
curSection.values.push(this._CreateValue(1000, this.lastString))
|
||||
this.lastString = null
|
||||
}
|
||||
|
||||
curSection.values.push(this._CreateValue(curr.code, curr.value))
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/** Finalize XDATA section parsing. */
|
||||
Finish(entity) {
|
||||
if (this.failure) {
|
||||
return
|
||||
}
|
||||
if (this.appName) {
|
||||
let xdata
|
||||
if (entity.hasOwnProperty("xdata")) {
|
||||
xdata = entity.xdata
|
||||
} else {
|
||||
xdata = {}
|
||||
entity.xdata = xdata
|
||||
}
|
||||
xdata[this.appName] = this.sectionStack[0]
|
||||
}
|
||||
}
|
||||
|
||||
get _currentSection() {
|
||||
return this.sectionStack[this.sectionStack.length - 1]
|
||||
}
|
||||
|
||||
_CreateSection() {
|
||||
return {
|
||||
values: []
|
||||
}
|
||||
}
|
||||
|
||||
_CreateValue(code, value) {
|
||||
return {code, value}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import AUTO_CAD_COLOR_INDEX from './AutoCadColorIndex';
|
||||
import ExtendedDataParser from './ExtendedDataParser';
|
||||
import DxfArrayScanner, { IGroup } from './DxfArrayScanner';
|
||||
import { IEntity, IPoint } from './entities/geomtry';
|
||||
|
||||
/**
|
||||
* 返回给定AutoCad颜色索引值的对应color值
|
||||
* @return {Number} color value as a number
|
||||
*/
|
||||
export function getAcadColor(index: number) {
|
||||
return AUTO_CAD_COLOR_INDEX[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析2D或3D坐标、矢量或点。完成后,扫描器保持在坐标的最后一组上。
|
||||
* @param {*} scanner
|
||||
*/
|
||||
export function parsePoint(scanner: DxfArrayScanner) {
|
||||
const point = {} as IPoint;
|
||||
|
||||
// 重读第一个坐标的组
|
||||
scanner.rewind();
|
||||
let curr = scanner.next();
|
||||
|
||||
let code = curr.code;
|
||||
point.x = curr.value as number;
|
||||
|
||||
code += 10;
|
||||
curr = scanner.next();
|
||||
if (curr.code != code)
|
||||
throw new Error('Expected code for point value to be ' + code +
|
||||
' but got ' + curr.code + '.');
|
||||
point.y = curr.value as number;
|
||||
|
||||
code += 10;
|
||||
curr = scanner.next();
|
||||
if (curr.code != code) {
|
||||
// Only the x and y are specified. Don't read z.
|
||||
scanner.rewind(); // Let the calling code advance off the point
|
||||
return point;
|
||||
}
|
||||
point.z = curr.value as number;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
/**
|
||||
* 某些实体可能包含由组101开始的嵌入对象。实体结束前的所有其他数据不应被解释为实体属性。该特性没有相关文档。
|
||||
* @param scanner
|
||||
*/
|
||||
export function skipEmbeddedObject(scanner: DxfArrayScanner) {
|
||||
/* 确保正确启动组. */
|
||||
scanner.rewind()
|
||||
let curr = scanner.next()
|
||||
if (curr.code !== 101) {
|
||||
throw new Error("Bad call for skipEmbeddedObject()")
|
||||
}
|
||||
do {
|
||||
curr = scanner.next()
|
||||
} while (curr.code !== 0)
|
||||
scanner.rewind()
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试解析所有实体通用的代码。如果组由此函数处理,则返回true。
|
||||
* @param {*} entity - the entity currently being parsed
|
||||
* @param {*} curr - the current group being parsed
|
||||
* @param scanner
|
||||
*/
|
||||
export function checkCommonEntityProperties(entity: IEntity, curr:IGroup, scanner:DxfArrayScanner) {
|
||||
let xdataParser;
|
||||
while (curr.code >= 1000) {
|
||||
if (!xdataParser) {
|
||||
xdataParser = new ExtendedDataParser()
|
||||
}
|
||||
if (xdataParser.Feed(curr)) {
|
||||
xdataParser.Finish(entity)
|
||||
xdataParser = null;
|
||||
} else {
|
||||
curr = scanner.next()
|
||||
}
|
||||
}
|
||||
if (xdataParser) {
|
||||
xdataParser.Finish(entity);
|
||||
scanner.rewind();
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (curr.code) {
|
||||
case 0:
|
||||
entity.type = curr.value as string;
|
||||
break;
|
||||
case 5:
|
||||
entity.handle = curr.value as number;
|
||||
break;
|
||||
case 6:
|
||||
entity.lineType = curr.value as string;
|
||||
break;
|
||||
case 8: // Layer name
|
||||
entity.layer = curr.value as string;
|
||||
break;
|
||||
case 48:
|
||||
entity.lineTypeScale = curr.value as number;
|
||||
break;
|
||||
case 60:
|
||||
entity.visible = curr.value === 0;
|
||||
break;
|
||||
case 62: // Acad Index Color. 0 inherits ByBlock. 256 inherits ByLayer. Default is bylayer
|
||||
entity.colorIndex = curr.value as number;
|
||||
entity.color = getAcadColor(Math.abs(curr.value as number));
|
||||
break;
|
||||
case 67:
|
||||
// 不存在或0表示图元位于模型空间中。
|
||||
// 1 表示图元位于图纸空间中(可选)
|
||||
entity.inPaperSpace = curr.value !== 0;
|
||||
break;
|
||||
case 100:
|
||||
//ignore
|
||||
break;
|
||||
case 101: // Embedded Object in ACAD 2018.
|
||||
// See https://ezdxf.readthedocs.io/en/master/dxfinternals/dxftags.html#embedded-objects
|
||||
while (curr.code != 0) {
|
||||
curr = scanner.next();
|
||||
}
|
||||
scanner.rewind();
|
||||
break;
|
||||
case 330:
|
||||
entity.ownerHandle = curr.value as string;
|
||||
break;
|
||||
case 347:
|
||||
entity.materialObjectHandle = curr.value as number;
|
||||
break;
|
||||
case 370:
|
||||
//From https://www.woutware.com/Forum/Topic/955/lineweight?returnUrl=%2FForum%2FUserPosts%3FuserId%3D478262319
|
||||
// An integer representing 100th of mm, must be one of the following values:
|
||||
// 0, 5, 9, 13, 15, 18, 20, 25, 30, 35, 40, 50, 53, 60, 70, 80, 90, 100, 106, 120, 140, 158, 200, 211.
|
||||
// -3 = STANDARD, -2 = BYLAYER, -1 = BYBLOCK
|
||||
entity.lineweight = curr.value as 0| 5| 9| 13| 15| 18| 20| 25| 30| 35| 40| 50| 53| 60| 70| 80| 90| 100| 106| 120| 140| 158| 200| 211|-3|-2|-1;
|
||||
break;
|
||||
case 420: // TrueColor Color
|
||||
entity.color = curr.value as number;
|
||||
break;
|
||||
case 1000:
|
||||
entity.extendedData = entity.extendedData || {};
|
||||
entity.extendedData.customStrings = entity.extendedData.customStrings || [];
|
||||
entity.extendedData.customStrings.push(curr.value as string);
|
||||
break;
|
||||
case 1001:
|
||||
entity.extendedData = entity.extendedData || {};
|
||||
entity.extendedData.applicationName = curr.value as string;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface I3DfaceEntity extends IEntity {
|
||||
shape: boolean;
|
||||
hasContinuousLinetypePattern: boolean;
|
||||
vertices: IPoint[];
|
||||
}
|
||||
|
||||
export default class ThreeDface implements IGeometry {
|
||||
public ForEntityName = '3DFACE' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value as string, vertices: [] as IPoint[] } as I3DfaceEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
switch (curr.code) {
|
||||
case 70: // 1 = Closed shape, 128 = plinegen?, 0 = default
|
||||
entity.shape = (((curr.value as number) & 1) === 1);
|
||||
entity.hasContinuousLinetypePattern = (((curr.value as number) & 128) === 128);
|
||||
break;
|
||||
case 10: // X coordinate of point
|
||||
entity.vertices = parse3dFaceVertices(scanner, curr);
|
||||
curr = scanner.lastReadGroup as IGroup;
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
function parse3dFaceVertices(scanner:DxfArrayScanner, curr:IGroup) {
|
||||
var vertices:IPoint[] = [];
|
||||
var vertexIsStarted = false;
|
||||
var vertexIsFinished = false;
|
||||
var verticesPer3dFace = 4; // there can be up to four vertices per face, although 3 is most used for TIN
|
||||
|
||||
for (let i = 0; i <= verticesPer3dFace; i++) {
|
||||
var vertex = {} as IPoint;
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0 || vertexIsFinished) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 10: // X0
|
||||
case 11: // X1
|
||||
case 12: // X2
|
||||
case 13: // X3
|
||||
if (vertexIsStarted) {
|
||||
vertexIsFinished = true;
|
||||
continue;
|
||||
}
|
||||
vertex.x = curr.value as number;
|
||||
vertexIsStarted = true;
|
||||
break;
|
||||
case 20: // Y
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
vertex.y = curr.value as number;
|
||||
break;
|
||||
case 30: // Z
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
vertex.z = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
// it is possible to have entity codes after the vertices.
|
||||
// So if code is not accounted for return to entity parser where it might be accounted for
|
||||
return vertices;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
// See https://groups.google.com/forum/#!topic/comp.cad.autocad/9gn8s5O_w6E
|
||||
vertices.push(vertex);
|
||||
vertexIsStarted = false;
|
||||
vertexIsFinished = false;
|
||||
}
|
||||
scanner.rewind();
|
||||
return vertices;
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IArcEntity extends IEntity {
|
||||
center: IPoint;
|
||||
radius: number;
|
||||
startAngle: number;
|
||||
endAngle: number;
|
||||
angleLength: number;
|
||||
extrusionDirectionX: number;
|
||||
extrusionDirectionY: number;
|
||||
extrusionDirectionZ: number;
|
||||
}
|
||||
|
||||
export default class Arc implements IGeometry {
|
||||
public ForEntityName = 'ARC' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value } as IArcEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 10: // X coordinate of point
|
||||
entity.center = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 40: // radius
|
||||
entity.radius = curr.value as number;
|
||||
break;
|
||||
case 50: // start angle
|
||||
entity.startAngle = Math.PI / 180 * (curr.value as number);
|
||||
break;
|
||||
case 51: // end angle
|
||||
entity.endAngle = Math.PI / 180 * (curr.value as number);
|
||||
entity.angleLength = entity.endAngle - entity.startAngle; // angleLength is deprecated
|
||||
break;
|
||||
case 210:
|
||||
entity.extrusionDirectionX = curr.value as number;
|
||||
break;
|
||||
case 220:
|
||||
entity.extrusionDirectionY = curr.value as number;
|
||||
break;
|
||||
case 230:
|
||||
entity.extrusionDirectionZ = curr.value as number;
|
||||
break;
|
||||
default: // ignored attribute
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IAttdefEntity extends IEntity {
|
||||
scale: number;
|
||||
textStyle: 'STANDARD' | string;
|
||||
text: string;
|
||||
tag: string;
|
||||
prompt: string;
|
||||
startPoint: IPoint;
|
||||
endPoint: IPoint;
|
||||
thickness: number;
|
||||
textHeight: number;
|
||||
rotation: number;
|
||||
obliqueAngle: number;
|
||||
invisible: boolean;
|
||||
constant: boolean;
|
||||
verificationRequired: boolean;
|
||||
preset: boolean;
|
||||
backwards: boolean;
|
||||
mirrored: boolean;
|
||||
horizontalJustification: number;
|
||||
fieldLength: number;
|
||||
verticalJustification: number;
|
||||
extrusionDirectionX: number;
|
||||
extrusionDirectionY: number;
|
||||
extrusionDirectionZ: number;
|
||||
}
|
||||
|
||||
export default class Attdef implements IGeometry {
|
||||
public ForEntityName = 'ATTDEF' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = {
|
||||
type: curr.value,
|
||||
scale: 1,
|
||||
textStyle: 'STANDARD'
|
||||
} as IAttdefEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) {
|
||||
break;
|
||||
}
|
||||
switch (curr.code) {
|
||||
case 1:
|
||||
entity.text = curr.value as string;
|
||||
break;
|
||||
case 2:
|
||||
entity.tag = curr.value as string;
|
||||
break;
|
||||
case 3:
|
||||
entity.prompt = curr.value as string;
|
||||
break;
|
||||
case 7:
|
||||
entity.textStyle = curr.value as string;
|
||||
break;
|
||||
case 10: // X coordinate of 'first alignment point'
|
||||
entity.startPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 11: // X coordinate of 'second alignment point'
|
||||
entity.endPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 39:
|
||||
entity.thickness = curr.value as number;
|
||||
break;
|
||||
case 40:
|
||||
entity.textHeight = curr.value as number;
|
||||
break;
|
||||
case 41:
|
||||
entity.scale = curr.value as number;
|
||||
break;
|
||||
case 50:
|
||||
entity.rotation = curr.value as number;
|
||||
break;
|
||||
case 51:
|
||||
entity.obliqueAngle = curr.value as number;
|
||||
break;
|
||||
case 70:
|
||||
entity.invisible = !!((curr.value as number) & 0x01);
|
||||
entity.constant = !!((curr.value as number) & 0x02);
|
||||
entity.verificationRequired = !!((curr.value as number) & 0x04);
|
||||
entity.preset = !!((curr.value as number) & 0x08);
|
||||
break;
|
||||
case 71:
|
||||
entity.backwards = !!((curr.value as number) & 0x02);
|
||||
entity.mirrored = !!((curr.value as number) & 0x04);
|
||||
break;
|
||||
case 72:
|
||||
// TODO: enum values?
|
||||
entity.horizontalJustification = curr.value as number;
|
||||
break;
|
||||
case 73:
|
||||
entity.fieldLength = curr.value as number;
|
||||
break;
|
||||
case 74:
|
||||
// TODO: enum values?
|
||||
entity.verticalJustification = curr.value as number;
|
||||
break;
|
||||
case 100:
|
||||
break;
|
||||
case 210:
|
||||
entity.extrusionDirectionX = curr.value as number;
|
||||
break;
|
||||
case 220:
|
||||
entity.extrusionDirectionY = curr.value as number;
|
||||
break;
|
||||
case 230:
|
||||
entity.extrusionDirectionZ = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers';
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IAttribEntity extends IEntity {
|
||||
scale: number;
|
||||
textStyle: 'STANDARD' | string;
|
||||
text: string;
|
||||
tag: string;
|
||||
prompt: string;
|
||||
startPoint: IPoint;
|
||||
endPoint: IPoint;
|
||||
thickness: number;
|
||||
textHeight: number;
|
||||
rotation: number;
|
||||
lineSpacingFactor: number;
|
||||
fillBoxScale: number;
|
||||
annotationHeight: number;
|
||||
obliqueAngle: number;
|
||||
invisible: boolean;
|
||||
constant: boolean;
|
||||
verificationRequired: boolean;
|
||||
preset: boolean;
|
||||
horizontalJustification: number;
|
||||
verticalJustification: number;
|
||||
backgroundFillColor: string;
|
||||
attachmentPoint: number;
|
||||
lineSpacing: number;
|
||||
backgroundFillSetting: string;
|
||||
extrusionDirection: IPoint;
|
||||
lockPositionFlag: number;
|
||||
hardPointerId: number;
|
||||
}
|
||||
|
||||
export default class Attrib implements IGeometry{
|
||||
public ForEntityName = 'ATTRIB' as const;
|
||||
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = {
|
||||
type: curr.value,
|
||||
scale: 1,
|
||||
textStyle: 'STANDARD',
|
||||
} as IAttribEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
// 0 为图元类型 (MTEXT),不写入,此处当作 Attrib解析
|
||||
if (curr.code === 0) {
|
||||
break;
|
||||
}
|
||||
switch (curr.code) {
|
||||
case 1:
|
||||
// 字符串本身
|
||||
entity.text = curr.value as string;
|
||||
break;
|
||||
case 2:
|
||||
// 属性标签(字符串;不能包含空格)
|
||||
entity.tag = curr.value as string;
|
||||
break;
|
||||
case 3:
|
||||
// 附加文字(始终在长度为 250 个字符的数据块中)(可选)
|
||||
entity.prompt = curr.value as string;
|
||||
break;
|
||||
case 7:
|
||||
// DXF:X 值;APP:三维矢量文字样式名(如果未提供,则为 STANDARD)(可选)
|
||||
entity.textStyle = curr.value as string;
|
||||
break;
|
||||
case 10: // 文字起点(在 OCS 中)
|
||||
entity.startPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 11: //X 轴方向矢量(在 WCS 中)
|
||||
entity.endPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 39:
|
||||
// 厚度(可选;默认值 = 0)
|
||||
entity.thickness = curr.value as number;
|
||||
break;
|
||||
case 40:
|
||||
// 文字高度
|
||||
entity.textHeight = curr.value as number;
|
||||
break;
|
||||
case 41:
|
||||
// X 相对缩放比例(宽度)(可选;默认值 = 1)。使用拟合类型的文字时,该值也将进行调整。
|
||||
entity.scale = curr.value as number;
|
||||
break;
|
||||
case 44:
|
||||
// 多行文字的行距比例(可选):
|
||||
// 要应用的行距的默认百分比(五分之三)。
|
||||
// 有效值的范围从 0.25 到 4.00
|
||||
entity.lineSpacingFactor = curr.value as number;
|
||||
break;
|
||||
case 45:
|
||||
// 填充框大小(可选):
|
||||
// 确定文字周围的边框大小。
|
||||
entity.fillBoxScale = curr.value as number;
|
||||
break;
|
||||
case 46:
|
||||
// 定义注释高度
|
||||
entity.annotationHeight = curr.value as number;
|
||||
break;
|
||||
case 50:
|
||||
//文字旋转角度(可选;默认值 = 0)
|
||||
entity.rotation = curr.value as number;
|
||||
break;
|
||||
case 51:
|
||||
// 倾斜角(可选;默认值 = 0)
|
||||
entity.obliqueAngle = curr.value as number;
|
||||
break;
|
||||
case 63:
|
||||
//背景填充颜色(可选):
|
||||
// 组码 90 为 1 时要用于背景填充的颜色。
|
||||
entity.backgroundFillColor = curr.value as string
|
||||
break;
|
||||
case 70:
|
||||
entity.invisible = !!((curr.value as number) & 0x01);
|
||||
entity.constant = !!((curr.value as number) & 0x02);
|
||||
entity.verificationRequired = !!((curr.value as number) & 0x04);
|
||||
entity.preset = !!((curr.value as number) & 0x08);
|
||||
break;
|
||||
case 71:
|
||||
//附着点:
|
||||
// 1 = 左上;2 = 中上;3 = 右上
|
||||
// 4 = 左中;5 = 正中;6 = 右中
|
||||
// 7 = 左下;8 = 中下;9 = 右下
|
||||
entity.attachmentPoint = curr.value as number;
|
||||
break;
|
||||
case 72:
|
||||
// 文字水平对正类型(可选;默认值 = 0)
|
||||
entity.horizontalJustification = curr.value as number;
|
||||
break;
|
||||
case 73:
|
||||
// 字段长度(可选;默认值 = 0)(当前未使用)
|
||||
entity.lineSpacing = curr.value as number;
|
||||
break;
|
||||
case 74:
|
||||
// 垂直文字对正类型(可选;默认值 = 0)
|
||||
entity.verticalJustification = curr.value as number;
|
||||
break;
|
||||
case 90:
|
||||
// 背景填充设置:
|
||||
// 0 = 背景填充关闭
|
||||
// 1 = 使用背景填充颜色
|
||||
// 2 = 使用图形窗口颜色作为背景填充颜色
|
||||
entity.backgroundFillSetting = curr.value as string;
|
||||
break;
|
||||
case 210:
|
||||
// 拉伸方向。仅当图元的拉伸方向与 WCS 的 Z 轴不平行时才出现(可选;默认值 = 0, 0, 1)
|
||||
entity.extrusionDirection = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 280:
|
||||
// 锁定位置标志。锁定块参照中属性的位置
|
||||
entity.lockPositionFlag = curr.value as number;
|
||||
break;
|
||||
case 340:
|
||||
// 次要属性或属性定义的硬指针 ID
|
||||
entity.hardPointerId = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface ICircleEntity extends IEntity {
|
||||
center: IPoint;
|
||||
radius: number;
|
||||
startAngle: number;
|
||||
endAngle: number;
|
||||
angleLength: number;
|
||||
}
|
||||
|
||||
export default class Circle implements IGeometry {
|
||||
public ForEntityName = 'CIRCLE' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value } as ICircleEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 10: // X coordinate of point
|
||||
entity.center = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 40: // radius
|
||||
entity.radius = (curr.value as number);
|
||||
break;
|
||||
case 50: // start angle
|
||||
entity.startAngle = Math.PI / 180 * (curr.value as number);
|
||||
break;
|
||||
case 51: // end angle
|
||||
const endAngle = Math.PI / 180 * (curr.value as number);
|
||||
if (endAngle < entity.startAngle)
|
||||
entity.angleLength = endAngle + 2 * Math.PI - entity.startAngle;
|
||||
else
|
||||
entity.angleLength = endAngle - entity.startAngle;
|
||||
entity.endAngle = endAngle;
|
||||
break;
|
||||
default: // ignored attribute
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IDimensionEntity extends IEntity{
|
||||
block: string;
|
||||
anchorPoint: IPoint;
|
||||
middleOfText: IPoint;
|
||||
insertionPoint: IPoint;
|
||||
linearOrAngularPoint1: IPoint;
|
||||
linearOrAngularPoint2: IPoint;
|
||||
diameterOrRadiusPoint: IPoint;
|
||||
arcPoint: IPoint;
|
||||
dimensionType: number;
|
||||
attachmentPoint: number;
|
||||
actualMeasurement: number;
|
||||
text: string;
|
||||
angle: number;
|
||||
}
|
||||
|
||||
export default class Dimension implements IGeometry {
|
||||
public ForEntityName = 'DIMENSION' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value } as IDimensionEntity;
|
||||
curr = scanner.next();
|
||||
while(!scanner.isEOF()) {
|
||||
if(curr.code === 0) break;
|
||||
|
||||
switch(curr.code) {
|
||||
case 2: // Referenced block name
|
||||
entity.block = curr.value as string;
|
||||
break;
|
||||
case 10: // X coordinate of 'first alignment point'
|
||||
entity.anchorPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 11:
|
||||
entity.middleOfText = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 12: // Insertion point for clones of a dimension
|
||||
entity.insertionPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 13: // Definition point for linear and angular dimensions
|
||||
entity.linearOrAngularPoint1 = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 14: // Definition point for linear and angular dimensions
|
||||
entity.linearOrAngularPoint2 = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 15: // Definition point for diameter, radius, and angular dimensions
|
||||
entity.diameterOrRadiusPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 16: // Point defining dimension arc for angular dimensions
|
||||
entity.arcPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 70: // Dimension type
|
||||
entity.dimensionType = curr.value as number;
|
||||
break;
|
||||
case 71: // 5 = Middle center
|
||||
entity.attachmentPoint = curr.value as number;
|
||||
break;
|
||||
case 42: // Actual measurement
|
||||
entity.actualMeasurement = curr.value as number;
|
||||
break;
|
||||
case 1: // Text entered by user explicitly
|
||||
entity.text = curr.value as string;
|
||||
break;
|
||||
case 50: // Angle of rotated, horizontal, or vertical dimensions
|
||||
entity.angle = curr.value as number;
|
||||
break;
|
||||
default: // check common entity attributes
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IEllipseEntity extends IEntity {
|
||||
center: IPoint;
|
||||
majorAxisEndPoint: IPoint;
|
||||
axisRatio: number;
|
||||
startAngle: number;
|
||||
endAngle: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default class Ellipse implements IGeometry {
|
||||
public ForEntityName = 'ELLIPSE' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value } as IEllipseEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 10:
|
||||
entity.center = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 11:
|
||||
entity.majorAxisEndPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 40:
|
||||
entity.axisRatio = curr.value as number;
|
||||
break;
|
||||
case 41:
|
||||
entity.startAngle = curr.value as number;
|
||||
break;
|
||||
case 42:
|
||||
entity.endAngle = curr.value as number;
|
||||
break;
|
||||
case 2:
|
||||
entity.name = curr.value as string;
|
||||
break;
|
||||
default: // check common entity attributes
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import DxfArrayScanner, { IGroup } from "../DxfArrayScanner";
|
||||
|
||||
export interface IPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
}
|
||||
|
||||
export interface IEntity {
|
||||
lineType: string;
|
||||
layer: string;
|
||||
lineTypeScale: number;
|
||||
visible: boolean;
|
||||
colorIndex: number;
|
||||
color: number;
|
||||
inPaperSpace: boolean;
|
||||
ownerHandle: string;
|
||||
materialObjectHandle: number;
|
||||
lineweight: 0| 5| 9| 13| 15| 18| 20| 25| 30| 35| 40| 50| 53| 60| 70| 80| 90| 100| 106| 120| 140| 158| 200| 211|-3|-2|-1;
|
||||
extendedData: {
|
||||
customStrings: string[];
|
||||
applicationName: string;
|
||||
};
|
||||
type: string;
|
||||
handle: number;
|
||||
}
|
||||
|
||||
export type EntityName = 'POINT'
|
||||
| '3DFACE'
|
||||
| 'ARC'
|
||||
| 'ATTDEF'
|
||||
| 'ATTRIB'
|
||||
| 'CIRCLE'
|
||||
| 'DIMENSION'
|
||||
| 'ELLIPSE'
|
||||
| 'HATCH'
|
||||
| 'INSERT'
|
||||
| 'LINE'
|
||||
| 'LWPOLYLINE'
|
||||
| 'MTEXT'
|
||||
| 'POLYLINE'
|
||||
| 'SOLID'
|
||||
| 'SPLINE'
|
||||
| 'TEXT'
|
||||
| 'VERTEX';
|
||||
|
||||
export default interface IGeometry {
|
||||
ForEntityName: EntityName;
|
||||
parseEntity(scanner: DxfArrayScanner, curr: IGroup): IEntity;
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
import DxfArrayScanner, {IGroup} from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, {IEntity, IPoint} from './geomtry';
|
||||
|
||||
export interface IHatchEntity extends IEntity {
|
||||
boundaryLoops: any[];
|
||||
definitionLines: any[];
|
||||
seedPoints: any[];
|
||||
patternName: string;
|
||||
isSolid: boolean;
|
||||
hatchStyle: string;
|
||||
patternType: number;
|
||||
patternAngle: number;
|
||||
patternScale: number;
|
||||
}
|
||||
|
||||
export default class Hatch implements IGeometry {
|
||||
public ForEntityName = 'HATCH' as const;
|
||||
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
let entity = {type: curr.value} as IHatchEntity;
|
||||
|
||||
let numBoundaryLoops = 0;
|
||||
let numDefinitionLines = 0;
|
||||
let numSeedPoints = 0;
|
||||
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
while (numBoundaryLoops > 0) {
|
||||
const loop = ParseBoundaryLoop(curr, scanner)
|
||||
if (loop) {
|
||||
entity.boundaryLoops.push(loop);
|
||||
numBoundaryLoops--;
|
||||
curr = scanner.next();
|
||||
} else {
|
||||
numBoundaryLoops = 0
|
||||
}
|
||||
}
|
||||
|
||||
while (numDefinitionLines > 0) {
|
||||
const line = ParseDefinitionLine(curr, scanner)
|
||||
if (line) {
|
||||
entity.definitionLines.push(line);
|
||||
numDefinitionLines--;
|
||||
curr = scanner.next();
|
||||
} else {
|
||||
numDefinitionLines = 0
|
||||
}
|
||||
}
|
||||
|
||||
while (numSeedPoints > 0) {
|
||||
const pt = ParseSeedPoint(curr, scanner);
|
||||
if (pt) {
|
||||
entity.seedPoints.push(pt);
|
||||
numSeedPoints--;
|
||||
curr = scanner.next();
|
||||
} else {
|
||||
numSeedPoints = 0
|
||||
}
|
||||
}
|
||||
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 2: // 填充图案名
|
||||
entity.patternName = curr.value as string;
|
||||
break;
|
||||
case 70: // 实体填充标志(实体填充 = 1;图案填充 = 0)
|
||||
entity.isSolid = curr.value != 0;
|
||||
break;
|
||||
case 91: // 边界路径(环)数
|
||||
numBoundaryLoops = curr.value as number;
|
||||
if (numBoundaryLoops > 0) {
|
||||
entity.boundaryLoops = []
|
||||
}
|
||||
break;
|
||||
/**
|
||||
* 图案填充样式:
|
||||
* 0 = 对“奇数奇偶校验”区域进行图案填充(普通样式)
|
||||
* 1 = 仅对最外层区域进行图案填充(“外部”样式)
|
||||
* 2 = 对整个区域进行图案填充(“忽略”样式)
|
||||
*/
|
||||
case 75:
|
||||
entity.hatchStyle = curr.value as string;
|
||||
break;
|
||||
/**
|
||||
* 填充图案类型:
|
||||
* 0 = 用户定义;1 = 预定义;2 = 自定义
|
||||
*/
|
||||
case 76:
|
||||
entity.patternType = curr.value as number;
|
||||
break;
|
||||
case 52: // 填充图案角度(仅限图案填充)
|
||||
entity.patternAngle = (curr.value as number) * Math.PI / 180;
|
||||
break;
|
||||
case 41: // 填充图案比例或间距(仅限图案填充)
|
||||
entity.patternScale = curr.value as number;
|
||||
break;
|
||||
case 78: // 图案定义直线数
|
||||
numDefinitionLines = curr.value as number;
|
||||
if (numDefinitionLines > 0) {
|
||||
entity.definitionLines = []
|
||||
}
|
||||
break;
|
||||
case 98: // 种子点数
|
||||
numSeedPoints = curr.value as number;
|
||||
if (numSeedPoints > 0) {
|
||||
entity.seedPoints = []
|
||||
}
|
||||
break;
|
||||
default: // 检查通用实体属性
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
};
|
||||
}
|
||||
|
||||
function ParseBoundaryLoop(curr: IGroup, scanner: DxfArrayScanner) {
|
||||
let entity:any = null
|
||||
|
||||
const ParsePolyline = () => {
|
||||
const pl: { vertices: IPoint[], isClosed: boolean } = {vertices: [], isClosed: false};
|
||||
let numVertices = 0;
|
||||
while (true) {
|
||||
if (numVertices > 0) {
|
||||
for (let i = 0; i < numVertices; i++) {
|
||||
if (curr.code != 10) {
|
||||
break
|
||||
}
|
||||
const p = helpers.parsePoint(scanner)
|
||||
curr = scanner.next();
|
||||
if (curr.code == 42) {
|
||||
// @ts-ignore
|
||||
p.bulge = curr.value;
|
||||
curr = scanner.next();
|
||||
}
|
||||
pl.vertices.push(p)
|
||||
}
|
||||
return pl
|
||||
}
|
||||
|
||||
switch (curr.code) {
|
||||
case 73:
|
||||
pl.isClosed = curr.value as boolean;
|
||||
break;
|
||||
case 93:
|
||||
numVertices = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
return pl;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
|
||||
const ParseEdge = () => {
|
||||
if (curr.code != 72) {
|
||||
return null
|
||||
}
|
||||
const e:any = {type: curr.value}
|
||||
curr = scanner.next();
|
||||
const isSpline = e.type == 4;
|
||||
|
||||
while (true) {
|
||||
switch (curr.code) {
|
||||
case 10:
|
||||
if (isSpline) {
|
||||
if (!e.controlPoints) {
|
||||
e.controlPoints = [];
|
||||
}
|
||||
e.controlPoints.push(helpers.parsePoint(scanner));
|
||||
} else {
|
||||
e.start = helpers.parsePoint(scanner);
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
if (isSpline) {
|
||||
if (!e.fitPoints) {
|
||||
e.fitPoints = [];
|
||||
}
|
||||
e.fitPoints.push(helpers.parsePoint(scanner));
|
||||
} else {
|
||||
e.end = helpers.parsePoint(scanner);
|
||||
}
|
||||
break;
|
||||
case 40:
|
||||
if (isSpline) {
|
||||
if (!e.knotValues) {
|
||||
e.knotValues = [];
|
||||
}
|
||||
e.knotValues.push(curr.value);
|
||||
} else {
|
||||
e.radius = curr.value;
|
||||
}
|
||||
break;
|
||||
case 50:
|
||||
e.startAngle = (curr.value as number) * Math.PI / 180;
|
||||
break;
|
||||
case 51:
|
||||
e.endAngle = (curr.value as number) * Math.PI / 180;
|
||||
break;
|
||||
case 73:
|
||||
if (isSpline) {
|
||||
e.rational = curr.value;
|
||||
} else {
|
||||
e.isCcw = curr.value;
|
||||
}
|
||||
break;
|
||||
case 74:
|
||||
e.periodic = curr.value;
|
||||
break;
|
||||
case 94:
|
||||
e.degreeOfSplineCurve = curr.value;
|
||||
break;
|
||||
|
||||
//XXX暂时忽略一些群体,主要是样条
|
||||
case 95:
|
||||
case 96:
|
||||
case 42:
|
||||
case 97:
|
||||
break;
|
||||
default:
|
||||
return e
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
|
||||
let polylineParsed = false;
|
||||
let numEdges = 0;
|
||||
let numSourceRefs = 0;
|
||||
|
||||
while (true) {
|
||||
if (!entity) {
|
||||
if (curr.code != 92) {
|
||||
return null;
|
||||
}
|
||||
entity = {type: curr.value};
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
if ((entity.type & 2) && !polylineParsed) {
|
||||
entity.polyline = ParsePolyline()
|
||||
polylineParsed = true
|
||||
}
|
||||
|
||||
while (numEdges) {
|
||||
const edge = ParseEdge();
|
||||
if (edge) {
|
||||
entity.edges.push(edge);
|
||||
numEdges--;
|
||||
} else {
|
||||
numEdges = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (numSourceRefs) {
|
||||
if (curr.code == 330) {
|
||||
entity.sourceRefs.push(curr.value);
|
||||
numSourceRefs--;
|
||||
curr = scanner.next();
|
||||
} else {
|
||||
numSourceRefs = 0
|
||||
}
|
||||
}
|
||||
|
||||
switch (curr.code) {
|
||||
case 93:
|
||||
numEdges = curr.value as number;
|
||||
if (numEdges > 0) {
|
||||
entity.edges = []
|
||||
}
|
||||
break;
|
||||
case 97:
|
||||
numSourceRefs = curr.value as number;
|
||||
if (numSourceRefs > 0) {
|
||||
entity.sourceRefs = []
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scanner.rewind();
|
||||
return entity;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
|
||||
function ParseDefinitionLine(curr: IGroup, scanner: DxfArrayScanner) {
|
||||
/* 假设总是从53组开始. */
|
||||
if (curr.code != 53) {
|
||||
return null
|
||||
}
|
||||
const entity:any = {
|
||||
angle: (curr.value as number) * Math.PI / 180,
|
||||
base: {x: 0, y: 0},
|
||||
offset: {x: 0, y: 0}
|
||||
};
|
||||
curr = scanner.next();
|
||||
|
||||
let numDashes = 0;
|
||||
while (true) {
|
||||
switch (curr.code) {
|
||||
case 43:
|
||||
entity.base.x = curr.value as number;
|
||||
break;
|
||||
case 44:
|
||||
entity.base.y = curr.value as number;
|
||||
break;
|
||||
case 45:
|
||||
entity.offset.x = curr.value as number;
|
||||
break;
|
||||
case 46:
|
||||
entity.offset.y = curr.value as number;
|
||||
break;
|
||||
case 49:
|
||||
if (numDashes > 0) {
|
||||
entity.dashes.push(curr.value);
|
||||
numDashes--;
|
||||
}
|
||||
break;
|
||||
case 79:
|
||||
numDashes = curr.value as number;
|
||||
if (curr.value) {
|
||||
entity.dashes = []
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scanner.rewind();
|
||||
return entity;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
}
|
||||
|
||||
function ParseSeedPoint(curr: IGroup, scanner: DxfArrayScanner) {
|
||||
if (curr.code != 10) {
|
||||
return null
|
||||
}
|
||||
return helpers.parsePoint(scanner);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IInsertEntity extends IEntity {
|
||||
name: string;
|
||||
xScale: number;
|
||||
yScale: number;
|
||||
zScale: number;
|
||||
position: IPoint;
|
||||
rotation: number;
|
||||
columnCount: number;
|
||||
rowCount: number;
|
||||
columnSpacing: number;
|
||||
rowSpacing: number;
|
||||
extrusionDirection: IPoint;
|
||||
}
|
||||
|
||||
export default class Insert implements IGeometry {
|
||||
public ForEntityName = 'INSERT' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value } as IInsertEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 2:
|
||||
entity.name = curr.value as string;
|
||||
break;
|
||||
case 41:
|
||||
entity.xScale = curr.value as number;
|
||||
break;
|
||||
case 42:
|
||||
entity.yScale = curr.value as number;
|
||||
break;
|
||||
case 43:
|
||||
entity.zScale = curr.value as number;
|
||||
break;
|
||||
case 10:
|
||||
entity.position = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 50:
|
||||
entity.rotation = curr.value as number;
|
||||
break;
|
||||
case 70:
|
||||
entity.columnCount = curr.value as number;
|
||||
break;
|
||||
case 71:
|
||||
entity.rowCount = curr.value as number;
|
||||
break;
|
||||
case 44:
|
||||
entity.columnSpacing = curr.value as number;
|
||||
break;
|
||||
case 45:
|
||||
entity.rowSpacing = curr.value as number;
|
||||
break;
|
||||
case 210:
|
||||
entity.extrusionDirection = helpers.parsePoint(scanner);
|
||||
break;
|
||||
default: // check common entity attributes
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface ILineEntity extends IEntity{
|
||||
vertices: IPoint[];
|
||||
extrusionDirection: IPoint;
|
||||
}
|
||||
|
||||
export default class Line implements IGeometry{
|
||||
public ForEntityName= 'LINE' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value, vertices: [] as IPoint[] } as ILineEntity;
|
||||
curr = scanner.next();
|
||||
while(!scanner.isEOF()) {
|
||||
if(curr.code === 0) break;
|
||||
|
||||
switch(curr.code) {
|
||||
case 10: // X coordinate of point
|
||||
entity.vertices.unshift(helpers.parsePoint(scanner));
|
||||
break;
|
||||
case 11:
|
||||
entity.vertices.push(helpers.parsePoint(scanner));
|
||||
break;
|
||||
case 210:
|
||||
entity.extrusionDirection = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 100:
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
|
||||
curr = scanner.next();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IVertex extends IPoint{
|
||||
startWidth: number;
|
||||
endWidth: number;
|
||||
bulge: number;
|
||||
}
|
||||
|
||||
export interface ILwpolylineEntity extends IEntity {
|
||||
vertices: IVertex[];
|
||||
elevation: number;
|
||||
depth: number;
|
||||
shape: boolean;
|
||||
hasContinuousLinetypePattern: boolean;
|
||||
width: number;
|
||||
extrusionDirectionX: number;
|
||||
extrusionDirectionY: number;
|
||||
extrusionDirectionZ: number;
|
||||
}
|
||||
|
||||
export default class Lwpolyline implements IGeometry {
|
||||
public ForEntityName = 'LWPOLYLINE' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value, vertices: [] as IVertex[] } as ILwpolylineEntity;
|
||||
let numberOfVertices = 0;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 38:
|
||||
entity.elevation = curr.value as number;
|
||||
break;
|
||||
case 39:
|
||||
entity.depth = curr.value as number;
|
||||
break;
|
||||
case 70: // 1 = Closed shape, 128 = plinegen?, 0 = default
|
||||
entity.shape = (((curr.value as number) & 1) === 1);
|
||||
entity.hasContinuousLinetypePattern = (((curr.value as number) & 128) === 128);
|
||||
break;
|
||||
case 90:
|
||||
numberOfVertices = curr.value as number;
|
||||
break;
|
||||
case 10: // X coordinate of point
|
||||
entity.vertices = parseLWPolylineVertices(numberOfVertices, scanner);
|
||||
break;
|
||||
case 43:
|
||||
if (curr.value !== 0) entity.width = curr.value as number;
|
||||
break;
|
||||
case 210:
|
||||
entity.extrusionDirectionX = curr.value as number;
|
||||
break;
|
||||
case 220:
|
||||
entity.extrusionDirectionY = curr.value as number;
|
||||
break;
|
||||
case 230:
|
||||
entity.extrusionDirectionZ = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
function parseLWPolylineVertices(n:number, scanner: DxfArrayScanner) {
|
||||
if (!n || n <= 0) throw Error('n must be greater than 0 verticies');
|
||||
const vertices = [] as IVertex[];
|
||||
let vertexIsStarted = false;
|
||||
let vertexIsFinished = false;
|
||||
let curr = scanner.lastReadGroup as IGroup;
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const vertex = {} as IVertex;
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0 || vertexIsFinished) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 10: // X
|
||||
if (vertexIsStarted) {
|
||||
vertexIsFinished = true;
|
||||
continue;
|
||||
}
|
||||
vertex.x = curr.value as number;
|
||||
vertexIsStarted = true;
|
||||
break;
|
||||
case 20: // Y
|
||||
vertex.y = curr.value as number;
|
||||
break;
|
||||
case 30: // Z
|
||||
vertex.z = curr.value as number;
|
||||
break;
|
||||
case 40: // start width
|
||||
vertex.startWidth = curr.value as number;
|
||||
break;
|
||||
case 41: // end width
|
||||
vertex.endWidth = curr.value as number;
|
||||
break;
|
||||
case 42: // bulge
|
||||
if (curr.value != 0) vertex.bulge = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
// if we do not hit known code return vertices. Code might belong to entity
|
||||
scanner.rewind();
|
||||
if (vertexIsStarted) {
|
||||
vertices.push(vertex);
|
||||
}
|
||||
scanner.rewind();
|
||||
return vertices;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
// See https://groups.google.com/forum/#!topic/comp.cad.autocad/9gn8s5O_w6E
|
||||
vertices.push(vertex);
|
||||
vertexIsStarted = false;
|
||||
vertexIsFinished = false;
|
||||
}
|
||||
scanner.rewind();
|
||||
return vertices;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IMtextEntity extends IEntity {
|
||||
text: string;
|
||||
position: IPoint;
|
||||
directionVector: IPoint;
|
||||
height: number;
|
||||
width: number;
|
||||
rotation: number;
|
||||
attachmentPoint: number;
|
||||
drawingDirection: number;
|
||||
}
|
||||
|
||||
export default class Mtext implements IGeometry {
|
||||
public ForEntityName = 'MTEXT' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value } as IMtextEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 3:
|
||||
entity.text ? entity.text += curr.value : entity.text = curr.value as string;
|
||||
break;
|
||||
case 1:
|
||||
entity.text ? entity.text += curr.value : entity.text = curr.value as string;
|
||||
break;
|
||||
case 10:
|
||||
entity.position = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 11:
|
||||
entity.directionVector = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 40:
|
||||
//Note: this is the text height
|
||||
entity.height = curr.value as number;
|
||||
break;
|
||||
case 41:
|
||||
entity.width = curr.value as number;
|
||||
break;
|
||||
case 50:
|
||||
entity.rotation = curr.value as number;
|
||||
break;
|
||||
case 71:
|
||||
entity.attachmentPoint = curr.value as number;
|
||||
break;
|
||||
case 72:
|
||||
entity.drawingDirection = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IPointEntity extends IEntity{
|
||||
position: IPoint;
|
||||
thickness: number;
|
||||
extrusionDirection: IPoint;
|
||||
}
|
||||
|
||||
export default class Point implements IGeometry{
|
||||
public ForEntityName= 'POINT' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const type = curr.value as string;
|
||||
const entity = { type } as unknown as IPointEntity;
|
||||
curr = scanner.next();
|
||||
while(!scanner.isEOF()) {
|
||||
if(curr.code === 0) break;
|
||||
|
||||
switch(curr.code) {
|
||||
case 10:
|
||||
entity.position = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 39:
|
||||
entity.thickness = curr.value as number;
|
||||
break;
|
||||
case 210:
|
||||
entity.extrusionDirection = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 100:
|
||||
break;
|
||||
default: // check common entity attributes
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import VertexParser, { IVertexEntity } from './vertex';
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
|
||||
export interface IPolylineEntity extends IEntity {
|
||||
vertices: IVertexEntity[];
|
||||
thickness: number;
|
||||
shape: boolean;
|
||||
includesCurveFitVertices: boolean;
|
||||
includesSplineFitVertices: boolean;
|
||||
is3dPolyline: boolean;
|
||||
is3dPolygonMesh: boolean;
|
||||
is3dPolygonMeshClosed: boolean;
|
||||
isPolyfaceMesh: boolean;
|
||||
hasContinuousLinetypePattern: boolean;
|
||||
extrusionDirection: IPoint;
|
||||
}
|
||||
|
||||
export default class Polyline implements IGeometry {
|
||||
public ForEntityName = 'POLYLINE' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
var entity = { type: curr.value, vertices: [] as IVertexEntity[] } as IPolylineEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 10: // always 0
|
||||
break;
|
||||
case 20: // always 0
|
||||
break;
|
||||
case 30: // elevation
|
||||
break;
|
||||
case 39: // thickness
|
||||
entity.thickness = curr.value as number;
|
||||
break;
|
||||
case 40: // start width
|
||||
break;
|
||||
case 41: // end width
|
||||
break;
|
||||
case 70:
|
||||
entity.shape = ((curr.value as number) & 1) !== 0;
|
||||
entity.includesCurveFitVertices = ((curr.value as number) & 2) !== 0;
|
||||
entity.includesSplineFitVertices = ((curr.value as number) & 4) !== 0;
|
||||
entity.is3dPolyline = ((curr.value as number) & 8) !== 0;
|
||||
entity.is3dPolygonMesh = ((curr.value as number) & 16) !== 0;
|
||||
entity.is3dPolygonMeshClosed = ((curr.value as number) & 32) !== 0; // 32 = The polygon mesh is closed in the N direction
|
||||
entity.isPolyfaceMesh = ((curr.value as number) & 64) !== 0;
|
||||
entity.hasContinuousLinetypePattern = ((curr.value as number) & 128) !== 0;
|
||||
break;
|
||||
case 71: // Polygon mesh M vertex count
|
||||
break;
|
||||
case 72: // Polygon mesh N vertex count
|
||||
break;
|
||||
case 73: // Smooth surface M density
|
||||
break;
|
||||
case 74: // Smooth surface N density
|
||||
break;
|
||||
case 75: // Curves and smooth surface type
|
||||
break;
|
||||
case 210:
|
||||
entity.extrusionDirection = helpers.parsePoint(scanner);
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
entity.vertices = parsePolylineVertices(scanner, curr);
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
function parsePolylineVertices(scanner:DxfArrayScanner, curr:IGroup) {
|
||||
const vertexParser = new VertexParser();
|
||||
|
||||
const vertices: IVertexEntity[] = [];
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) {
|
||||
if (curr.value === 'VERTEX') {
|
||||
vertices.push(vertexParser.parseEntity(scanner, curr));
|
||||
curr = scanner.lastReadGroup as IGroup;
|
||||
} else if (curr.value === 'SEQEND') {
|
||||
parseSeqEnd(scanner, curr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
function parseSeqEnd(scanner:DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value } as IEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code == 0) break;
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface ISolidEntity extends IEntity {
|
||||
points: IPoint[];
|
||||
extrusionDirection: IPoint;
|
||||
}
|
||||
|
||||
export default class Solid implements IGeometry {
|
||||
public ForEntityName = 'SOLID' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value, points: [] as IPoint[] } as ISolidEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 10:
|
||||
entity.points[0] = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 11:
|
||||
entity.points[1] = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 12:
|
||||
entity.points[2] = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 13:
|
||||
entity.points[3] = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 210:
|
||||
entity.extrusionDirection = helpers.parsePoint(scanner);
|
||||
break;
|
||||
default: // check common entity attributes
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface ISplineEntity extends IEntity {
|
||||
controlPoints?: IPoint[];
|
||||
fitPoints?: IPoint[];
|
||||
startTangent: IPoint;
|
||||
endTangent: IPoint;
|
||||
knotValues: number[];
|
||||
closed: boolean;
|
||||
periodic: boolean;
|
||||
rational: boolean;
|
||||
planar: boolean;
|
||||
linear: boolean;
|
||||
degreeOfSplineCurve: number;
|
||||
numberOfKnots: number;
|
||||
numberOfControlPoints: number;
|
||||
numberOfFitPoints: number;
|
||||
normalVector: IPoint;
|
||||
}
|
||||
|
||||
export default class Spline implements IGeometry {
|
||||
public ForEntityName = 'SPLINE' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = { type: curr.value } as ISplineEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
|
||||
switch (curr.code) {
|
||||
case 10:
|
||||
if (!entity.controlPoints) entity.controlPoints = [];
|
||||
entity.controlPoints.push(helpers.parsePoint(scanner));
|
||||
break;
|
||||
case 11:
|
||||
if (!entity.fitPoints) entity.fitPoints = [];
|
||||
entity.fitPoints.push(helpers.parsePoint(scanner));
|
||||
break;
|
||||
case 12:
|
||||
entity.startTangent = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 13:
|
||||
entity.endTangent = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 40:
|
||||
if (!entity.knotValues) entity.knotValues = [];
|
||||
entity.knotValues.push(curr.value as number);
|
||||
break;
|
||||
case 70:
|
||||
if (((curr.value as number) & 1) != 0) entity.closed = true;
|
||||
if (((curr.value as number) & 2) != 0) entity.periodic = true;
|
||||
if (((curr.value as number) & 4) != 0) entity.rational = true;
|
||||
if (((curr.value as number) & 8) != 0) entity.planar = true;
|
||||
if (((curr.value as number) & 16) != 0) {
|
||||
entity.planar = true;
|
||||
entity.linear = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 71:
|
||||
entity.degreeOfSplineCurve = curr.value as number;
|
||||
break;
|
||||
case 72:
|
||||
entity.numberOfKnots = curr.value as number;
|
||||
break;
|
||||
case 73:
|
||||
entity.numberOfControlPoints = curr.value as number;
|
||||
break;
|
||||
case 74:
|
||||
entity.numberOfFitPoints = curr.value as number;
|
||||
break;
|
||||
case 210:
|
||||
entity.normalVector = helpers.parsePoint(scanner);
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import DxfArrayScanner, {IGroup} from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
import IGeometry, {IEntity, IPoint} from './geomtry';
|
||||
|
||||
export interface ITextEntity extends IEntity {
|
||||
startPoint: IPoint;
|
||||
endPoint: IPoint;
|
||||
textHeight: number;
|
||||
xScale: number;
|
||||
rotation: number;
|
||||
obliqueAngle: number;
|
||||
text: string;
|
||||
fontName: string;
|
||||
thickness: number;
|
||||
halign: number;
|
||||
valign: number;
|
||||
flags: number;
|
||||
}
|
||||
|
||||
export default class Text implements IGeometry {
|
||||
public ForEntityName = 'TEXT' as const;
|
||||
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
const entity = {type: curr.value} as ITextEntity;
|
||||
curr = scanner.next();
|
||||
while (!scanner.isEOF()) {
|
||||
if (curr.code === 0) break;
|
||||
switch (curr.code) {
|
||||
case 1:
|
||||
// 字符串本身
|
||||
entity.text = curr.value as string;
|
||||
break;
|
||||
case 7: // 字体名称/文字样式名(可选;默认值 = 标准)
|
||||
entity.fontName = curr.value as string;
|
||||
break;
|
||||
case 10:
|
||||
// 第一对齐点(在 OCS 中) DXF:X 值;APP:三维点
|
||||
entity.startPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 11:
|
||||
// 第二对齐点(在 OCS 中)(可选);
|
||||
// DXF:X 值;APP:三维点;
|
||||
// 只有当 72 或 73 组的值非零时,该值才有意义(如果对正不是基线对正/左对正)
|
||||
entity.endPoint = helpers.parsePoint(scanner);
|
||||
break;
|
||||
case 39:
|
||||
// 厚度(可选;默认值 = 0)
|
||||
entity.thickness = curr.value as number;
|
||||
break;
|
||||
case 40:
|
||||
// 文字高度
|
||||
entity.textHeight = curr.value as number;
|
||||
break;
|
||||
case 41:
|
||||
// 相对 X 比例因子 — 宽度(可选;默认值 = 1)
|
||||
// 使用拟合类型的文字时,该值也将进行调整。
|
||||
entity.xScale = curr.value as number;
|
||||
break;
|
||||
case 50:
|
||||
// 文字旋转角度(可选;默认值 = 0)
|
||||
entity.rotation = curr.value as number;
|
||||
break;
|
||||
case 51:
|
||||
// 倾斜角度(可选;默认值 = 0)
|
||||
entity.obliqueAngle = curr.value as number;
|
||||
break;
|
||||
case 71:
|
||||
// 文字生成标志(可选,仅适用于 SHX 文字):
|
||||
// 2 = 文字反向(在 X 轴方向镜像)
|
||||
// 4 = 文字倒置(在 Y 轴方向镜像)
|
||||
entity.flags = curr.value as number;
|
||||
break;
|
||||
// NOTE: 72和73没有11就没有意义(第二个对齐点)
|
||||
case 72:
|
||||
// 文字水平对正类型(可选;默认值 = 0)整数代码(非按位编码)
|
||||
// 0 = 左对正;1 = 居中对正;2 = 右对正
|
||||
// 3 = 对齐(如果垂直对齐 = 0)
|
||||
// 4 = 中间(如果垂直对齐 = 0)
|
||||
// 5 = 拟合(如果垂直对齐 = 0)
|
||||
entity.halign = curr.value as number;
|
||||
break;
|
||||
case 73:
|
||||
// 文字垂直对正类型(可选;默认值 = 0)整数代码(不是按位编码)
|
||||
// 0 = 基线对正;1 = 底端对正;2 = 居中对正;3 = 顶端对正
|
||||
entity.valign = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
// check common entity attributes
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
curr = scanner.next();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
import DxfArrayScanner, { IGroup } from '../DxfArrayScanner';
|
||||
import * as helpers from '../ParseHelpers'
|
||||
|
||||
import IGeometry, { IEntity, IPoint } from './geomtry';
|
||||
|
||||
export interface IVertexEntity extends IEntity, IPoint{
|
||||
bulge: number;
|
||||
curveFittingVertex: boolean;
|
||||
curveFitTangent: boolean;
|
||||
splineVertex: boolean;
|
||||
splineControlPoint: boolean;
|
||||
threeDPolylineVertex: boolean;
|
||||
threeDPolylineMesh: boolean;
|
||||
polyfaceMeshVertex: boolean;
|
||||
faceA: number;
|
||||
faceB: number;
|
||||
faceC: number;
|
||||
faceD: number;
|
||||
}
|
||||
|
||||
export default class Vertex implements IGeometry {
|
||||
public ForEntityName= 'VERTEX' as const;
|
||||
public parseEntity(scanner: DxfArrayScanner, curr: IGroup) {
|
||||
var entity = { type: curr.value } as IVertexEntity;
|
||||
curr = scanner.next();
|
||||
while(!scanner.isEOF()) {
|
||||
if(curr.code === 0) break;
|
||||
|
||||
switch(curr.code) {
|
||||
case 10: // X
|
||||
entity.x = curr.value as number;
|
||||
break;
|
||||
case 20: // Y
|
||||
entity.y = curr.value as number;
|
||||
break;
|
||||
case 30: // Z
|
||||
entity.z = curr.value as number;
|
||||
break;
|
||||
case 40: // start width
|
||||
break;
|
||||
case 41: // end width
|
||||
break;
|
||||
case 42: // bulge
|
||||
if(curr.value != 0) entity.bulge = curr.value as number;
|
||||
break;
|
||||
case 70: // flags
|
||||
entity.curveFittingVertex = ((curr.value as number) & 1) !== 0;
|
||||
entity.curveFitTangent = ((curr.value as number) & 2) !== 0;
|
||||
entity.splineVertex = ((curr.value as number) & 8) !== 0;
|
||||
entity.splineControlPoint = ((curr.value as number) & 16) !== 0;
|
||||
entity.threeDPolylineVertex = ((curr.value as number) & 32) !== 0;
|
||||
entity.threeDPolylineMesh = ((curr.value as number) & 64) !== 0;
|
||||
entity.polyfaceMeshVertex = ((curr.value as number) & 128) !== 0;
|
||||
break;
|
||||
case 50: // curve fit tangent direction
|
||||
break;
|
||||
case 71: // polyface mesh vertex index
|
||||
entity.faceA = curr.value as number;
|
||||
break;
|
||||
case 72: // polyface mesh vertex index
|
||||
entity.faceB = curr.value as number;
|
||||
break;
|
||||
case 73: // polyface mesh vertex index
|
||||
entity.faceC = curr.value as number;
|
||||
break;
|
||||
case 74: // polyface mesh vertex index
|
||||
entity.faceD = curr.value as number;
|
||||
break;
|
||||
default:
|
||||
helpers.checkCommonEntityProperties(entity, curr, scanner);
|
||||
break;
|
||||
}
|
||||
|
||||
curr = scanner.next();
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import DxfParser from './DxfParser';
|
||||
export { default as DxfParser } from './DxfParser';
|
||||
export type { IDxf, IBlock, ILayerTypesTable, ILayersTable, ITables, IViewPortTable, IBaseTable, ILayer, ILayerTableDefinition, ILineType, ILineTypeTableDefinition, ITable, ITableDefinitions, IViewPort, IViewPortTableDefinition } from './DxfParser';
|
||||
export type { IEntity, IPoint } from './entities/geomtry';
|
||||
export type { I3DfaceEntity } from './entities/3dface';
|
||||
export type { IArcEntity } from './entities/arc';
|
||||
export type { IAttdefEntity } from './entities/attdef';
|
||||
export type { ICircleEntity } from './entities/circle';
|
||||
export type { IDimensionEntity } from './entities/dimension';
|
||||
export type { IEllipseEntity } from './entities/ellipse';
|
||||
export type { IInsertEntity } from './entities/insert';
|
||||
export type { ILineEntity } from './entities/line';
|
||||
export type { ILwpolylineEntity } from './entities/lwpolyline';
|
||||
export type { IMtextEntity } from './entities/mtext';
|
||||
export type { IPointEntity } from './entities/point';
|
||||
export type { IPolylineEntity } from './entities/polyline';
|
||||
export type { ISolidEntity } from './entities/solid';
|
||||
export type { ISplineEntity } from './entities/spline';
|
||||
export type { ITextEntity } from './entities/text';
|
||||
export type { IVertexEntity } from './entities/vertex';
|
||||
|
||||
export default DxfParser
|
||||
Reference in New Issue
Block a user