DXF and the classes
What each class becomes in a DXF file, what each entity of a file becomes when it is read, and how a frame, a layer and a version are kept.
Two sides: geometry and drawings
DXF is read and written by compiled code, src/__dxf__.cc,
which needs nothing but Octave. It takes the objects themselves and builds
them by their constructors, so there is no list of entities to handle in
between. The package meets DXF from two sides:
| Geometry | Drawings | |
|---|---|---|
| Classes | geom.Polyline,
geom.Spline, geom.Path,
geom.Region | draw.Drawing |
| Write | write (G, FILE) for one object,
geom.write (C, FILE) for a cell of them |
write (D, FILE) |
| Read | geom.read (FILE) |
draw.read (FILE) |
| For | Profiles to build solids from, parts for a laser or a router, outlines exchanged with a CAD program. Objects keep their frame in 3-D. | Technical drawings: dimensions, text, hatches, blocks, layers, line types and colours. Everything lies in the plane z = 0. |
Both sides write the same format, R2000 by default, and both read files from R12 to R2018, so a file written from one side can be read by the other.
Writing geom objects
Each geom class has a write method, and
geom.write writes a cell array of any mix of them in one pass.
The file name must end in .dxf.
| Class | Written as |
|---|---|
geom.Polyline | One LWPOLYLINE with
its bulges, its plane held as the entity's normal and elevation. |
geom.Spline | One SPLINE, with its
fit points where it was drawn through points. |
geom.Path | Its pieces, LINE,
ARC in its own plane and SPLINE, bound by a
GROUP. |
geom.Region | Its loops, the outline first, bound
by a GROUP: a loop of lines and arcs as one closed
LWPOLYLINE, a loop with a spline in it as its pieces. With
'Fill', true, a solid HATCH as well, written with
the group. |
Geom objects carry no layer, line type or colour of their own; the
options give them, one value for every object or, in
geom.write, a cell array of one per object:
| Option | Value |
|---|---|
'Layer' | The layer, '0' by
default. |
'Linetype' | 'CONTINUOUS' by
default, or a line type of draw.linetype: HIDDEN, CENTER,
PHANTOM, DASHED, DASHDOT, DOT. |
'Colour' | An AutoCAD colour index from 1 to 256, 256 meaning the layer's colour, the default. True colour came only with R2004. |
'Fill' | true adds a solid hatch
over a region; false by default. |
'Version' | 'R2000', the default,
or 'R12', which holds only lines and arcs, so only polylines
may be written as R12. |
'LTScale' | The line-type scale written in the header, 1 by default, so dashes look the same wherever the file is opened. |
R = geom.Region ([0, 0; 60, 0; 60, 40; 0, 40], {[20, 20, 1; 40, 20, 1]});
P = geom.Path ([0, 0, 0; 0, 0, 50; 30, 0, 80]);
write (R, 'plate.dxf', 'Layer', 'PLATE', 'Fill', true);
geom.write ({R, P}, 'parts.dxf', 'Layer', {'PLATE', 'ROUTE'});
A DXF file is not appended to. Adding to one rewrites its tables, its handles and its objects section, so a file is written whole, from every object it is to hold.
Reading geom objects
What is in the file
Without options, geom.read returns what is in the file: a
row cell array with one geom object per entity, in the order of the file,
nothing joined and nothing reclassified.
| Entity | Read as |
|---|---|
LINE, ARC, CIRCLE,
LWPOLYLINE, a flat POLYLINE |
geom.Polyline |
SPLINE, ELLIPSE |
geom.Spline, an ellipse as its exact rational
spline |
HATCH | The geom.Region objects
it fills |
A LINE slanting in 3-D, a 3-D
POLYLINE | geom.Path, since no single
plane holds them |
| A group the package wrote | One object, of the class its extended data names |
| Text, dimensions, inserts, points, meshes, solids | Skipped |
A group the package wrote comes back whole, so what was written is what is
read: a region keeps its holes, a closed path stays a path, and the hatch
written with 'Fill' is part of its region, never a second one.
'Layer' keeps only what lies on one layer, its name compared
without regard to case, as CAD programs compare them.
C = geom.read ('plate.dxf') # one region, with its bore
C = geom.read ('parts.dxf') # a region and a path
C = geom.read ('parts.dxf', 'Layer', 'route') # the path alone
Building one object from loose entities
A file from another program holds loose lines and arcs, not regions.
'Type' builds one object of a class from them:
'Type' | Builds |
|---|---|
'polyline' | A geom.Polyline, from
one entity. |
'spline' | A geom.Spline, from one
entity. |
'path' | A geom.Path, the entities
chained end to end by geom.Path.chain. |
'region' | A geom.Region, the
entities chained into closed loops and the loops that share a plane nested
by geom.Region.nest into an outline and its holes. |
Ends closer than 1e-4 mm are joined and snapped to one point, which is
above the rounding of coordinates in a file and below anything that can be
made. The package's own groups count as already built. Exactly one object
must result: more than one is an error naming the layer, as is finding
candidates on several layers with no 'Layer' given, and finding
none. Separate parts belong on separate layers, and 'Layer'
picks one.
Four separate lines, as a CAD program would leave them, become one region ready to extrude:
L = {geom.Polyline([0, 0; 50, 0]), geom.Polyline([50, 0; 50, 30]), ...
geom.Polyline([50, 30; 0, 30]), geom.Polyline([0, 30; 0, 0])};
geom.write (L, 'lines.dxf', 'Layer', 'PROFILE');
C = geom.read ('lines.dxf'); # four open polylines
R = geom.read ('lines.dxf', 'Type', 'region'); # one closed region
S = solid.extrude (R, 10);
Whatever is skipped, and with 'Type' whatever is left over,
is reported in one warning per read, counted by entity type.
Frames
Every geom object lies in a geom.UCS, and DXF has no place
for one, so the package keeps it as extended data under the registered
application DRAFTING: the origin in group 1011 and the x
axis in group 1013, and for a path, a spline or a region the normal in a
second 1013, since a flat entity keeps its normal in group 210. On a group,
the same data names the class, geom.Path or
geom.Region, in group 1000.
geom.read restores the frame, for a flat object only when the
stored normal still matches the entity's and the origin still lies in its
plane. The coordinates of the entities stay authoritative, so a frame left
stale by an edit elsewhere can never move the geometry.
## A region in the plane x = 5, written and read back in the same frame
T = geom.Region ([0, 0; 30, 0; 30, 20; 0, 20]);
T.UCS = geom.UCS ([1, 0, 0], [5, 0, 0]);
write (T, 'tilted.dxf');
B = geom.read ('tilted.dxf', 'Type', 'region');
B.UCS == T.UCS
A file without that data is read in any plane. An entity whose normal is the world z axis is moved to its height. One whose normal points down the z axis is mirrored into the plane facing up first, as 2-D CAD programs treat it. Any other is read in DXF's own frame for its plane. Paths and splines are read in world coordinates.
Drawings
write (D, FILE) writes a draw.Drawing with its
layer table and every entity on its layer. Each entity is written as
itself:
| Entity in the drawing | Written as |
|---|---|
| line, point, arc, circle, ellipse, text, spline | The DXF entity of the same name |
| polyline | LWPOLYLINE, its bulges kept |
| path, region | Its pieces or loops, bound by a group that names the class |
| hatch | HATCH over its region, the holes left
clear, with its pattern, angle and spacing |
| dimension | DIMENSION, its picture in an
anonymous block, so a CAD program measures it again; the picture on layer
0 and by block, so the dimension's own layer governs it |
| centre mark, leader | The lines and text they are drawn with |
| block and insert | The block once, placed by
INSERT however often it is used |
A drawing writes no frames, since everything in it lies in z = 0: its path and region groups carry only their class name.
Layers
A drawing keeps a layer table, as a DXF file does, and the two map one to one:
In D.Layers | In the file |
|---|---|
colour | The layer's colour, group 62 |
linetype | The layer's line type, group 6,
declared in the LTYPE table |
lineweight | Group 370, the nearest of the weights DXF allows; none in R12 |
visible false | The colour negated, the layer switched off; a frozen layer reads as not visible too |
plot false | Group 290 set to 0, the layer not printed |
An entity drawn 'byLayer', as entities are unless told
otherwise, is written by layer: no line type, colour or weight of its own,
so a CAD program that changes a layer changes everything on it. An entity
with a colour, line type or weight of its own is written with it. Read back,
the table returns whole and by-layer entities stay by layer.
| Option | Value |
|---|---|
'Version' | 'R2000', the default,
or 'R12'; a drawing holding an ellipse, a spline, a path, a
region or a hatch is an error under R12, naming what it cannot
hold. |
'Dimensions' | 'associative', the
default, or 'explode' to write each dimension as its lines
and text, for a program that cannot read a DIMENSION. |
'Blocks' | 'reference', the
default, or 'expand' to copy each block's contents in place of
every insert, for a program that ignores INSERT. |
'DimScale' | Multiplies the dimension ornament: a drawing meant for 1:50 wants 50. 1 by default. |
'LTScale' | The line-type scale, 1 by default. |
draw.read gives the drawing back as a
draw.Drawing named 'imported', with the file's
layer table and each entity on its layer. Dimensions return as dimensions, rebuilt
from their definition points as the method that would have made them, so
they measure the geometry again rather than repeating a number. Blocks
return with their inserts, nested blocks included, hatches over their
regions, and a path or region the package wrote as that path or region. A
hatch pattern the package does not define is drawn as
'ANSI31', and an MTEXT returns as one line of text,
its formatting removed.
R = geom.Region ([0, 0; 60, 0; 60, 40; 0, 40], {[20, 20, 1; 40, 20, 1]});
D = draw.Drawing ('plate');
D.Layer = 'OUTLINE';
D = D.region (R);
D.Layer = 'DIMENSIONS';
D = D.dim ([0, 0], [60, 0], -10, 'horizontal');
D = D.diam ([30, 20], 10);
write (D, 'drawing.dxf');
E = draw.read ('drawing.dxf'); # the region and two dimensions
layers (E) # DIMENSIONS and OUTLINE
write (D, 'exploded.dxf', 'Dimensions', 'explode');
A drawing lies in the plane z = 0, so draw.read
mirrors an entity facing down into the plane facing up and drops heights.
An entity on any other plane, a mesh, a solid, an entity in paper space and
an insert of a block the file does not define are skipped and reported. A
geom object can go into a drawing only if it lies in the xy plane;
one in another plane is written with geom.write instead.
Versions, units and text
- Written: R2000 (
AC1015) by default, which holds what the package draws as itself:LWPOLYLINEwith its bulges,SPLINE,ELLIPSE,HATCH, groups and extended data. R12 (AC1009) on request, for lines and arcs only. - Read: ASCII DXF from R12 (
AC1009) to R2018 (AC1032). A binary DXF is an error. - Units: the writer declares millimetres. The reader converts
coordinates to millimetres from the units the file declares in
$INSUNITS, and reads a file declaring none as millimetres. - Text: written with every character outside ASCII escaped as
\U+XXXX, so it reads the same whatever the reader's code page. Read from the code page the header names before R2007, and as UTF-8 from R2007 on.