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

981 lines
36 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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