Files
TkAstral3D/packages/sdk/lib/dxf/parser/DxfParser.ts
T
2025-10-04 23:36:07 +08:00

981 lines
36 KiB
TypeScript

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