feat(All):Initial
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user