Drawing read
Four read-only tools that let an agent read a 2D engineering drawing as a drawing: its entities, layers, dimensions and measurements, as coordinates and numbers, never as an image. They are built on OCCTSwiftIO’s DXF import (SwiftDXF’s entity model) and run in-process. A drawing is a file the agent reads, not a scene body: none of these tools touches the scene or manifest.json.
Tools
Server: Swift only. The Node server has no drawing reader.
Every tool takes inputPath, the absolute path of an ASCII DXF file (a leading ~ is expanded). Parsed drawings are cached by path, size and modification time, so a run of calls on one file parses it once and an edited file is read again. An edit that keeps the size and lands within the filesystem’s timestamp resolution of the previous read would be served from the cache; the sha256 in every result says which bytes were read.
Arguments must have the right type (types, layers, kinds arrays of strings, rect four numbers, limit and offset integers, mmPerUnit a number); a wrong type is an error, not an ignored argument; so is an argument name the tool does not take (layer for layers) and a limit outside 1 to 5000. JSON null means the argument is absent. An entity id has one spelling, exactly what the tools return, so e+5 and e007 are refused. A coordinate that is not finite (a 1e999 in the file) is written as the string "inf", "-inf" or "nan" instead of failing the result.
What is read, and what is not
- DXF (ASCII) is read:
LINE,CIRCLE,ARC,ELLIPSE,POINT,TEXT/MTEXT,LWPOLYLINE/POLYLINE,SPLINEandDIMENSIONfrom theENTITIESsection. The reader does not tell model space from paper space, so entities from both appear. Binary DXF is refused with a message. - DWG and JWW are not read yet.
DWGLoaderis merged in OCCTSwiftIO (#53) but is not in a tagged release at this server’s pin, and JWW has no entity model there yet (#54). Both return an error that says so. Convert the file to ASCII DXF meanwhile. - Extrusion directions are not read. SwiftDXF 0.3 ignores DXF groups 210, 220 and 230, so an entity drawn in a mirrored or rotated coordinate system is read at its stored coordinates, unflipped, and nothing warns about it. SwiftDXF 0.4.0 reads them.
- SwiftDXF is held on the 0.3 line. OCCTSwiftIO 2.0.0-beta.5 requires SwiftDXF 0.3.0, but its floor admits 0.4.0, which it does not compile against (OCCTSwiftIO#60). This package declares SwiftDXF
.upToNextMinor(from: "0.3.0"), so a resolve gives 0.3.0 and a host that embedsOCCTMCPCoregets the same. Moving to 0.4.0 (entity handles, hatch and leader entities, computed dimension values, block expansion) is tracked as OCCTMCP#271. - A newer SwiftDXF may add entity types. An entity the tools have no mapping for is listed with type
otherand no layer, colour, bounds or geometry (nothing is made up), a layer filter never matches it,read_drawingwarns about it andmeasure_drawingrefuses it. - What the reader skips is reported, not hidden. SwiftDXF models the entity types above and drops the rest of the
ENTITIESsection (INSERT,HATCH,LEADERand so on) and does not read theBLOCKSsection, so a block insert places no geometry.read_drawingreports what SwiftDXF skipped (Drawing.skipped), by type and by layer, and warns when block inserts are among them. SwiftDXF counts anINSERT’sATTRIBrecords as skipped entities of their own, so a block insert with attributes shows the attributes as well.
Identity and units
- Entity ids. An entity’s id is
eand its position in the drawing’s entity list (e0,e1, …). SwiftDXF keeps no DXF handles, so the id is the index. It is stable for the same file bytes and this reader:read_drawingreturns the file’s SHA-256, and every result carries the file name, hash and SwiftDXF release line so a caller can tell when ids no longer mean what they did.source.readerVersionis that line (0.3, major and minor: the part that decides which entity types are modelled). A newer SwiftDXF that models more entity types shifts the ids after them: when SwiftDXF 0.3.0 began to modelSPLINE, a spline took an index, so the ids after one in the same file are one higher than they were under 0.2. Skipped records take no index. - Units. Coordinates and values are in drawing units. The drawing’s units are the DXF
$INSUNITScode, returned with its name and millimetres per unit (codes 0 to 24 as ezdxf lists them). A unitless or undeclared drawing has no millimetre figure and the tools say so; passmmPerUnittomeasure_drawingto supply one.
read_drawing
Summarise a drawing: counts, layers, units, extents, the source record and what was skipped. Start here.
| Name | Type | Required | Description |
|---|---|---|---|
inputPath | string | yes | Absolute path of the DXF file. |
Returns: entityCount, byType, layers (each with its name, total and counts by type), units (insUnits, name, mmPerUnit), extents (declared from the header’s $EXTMIN / $EXTMAX, computed from the entities, which can differ when an editor left the header stale), source (path, fileName, bytes, sha256, format, dxfVersion, reader, readerVersion), skipped (total, byType, byLayer) and warnings.
Example
{ "inputPath": "/Users/me/drawings/slot.dxf" }
{
"entityCount": 4,
"byType": { "line": 2, "circle": 1, "dimension": 1 },
"layers": [
{ "name": "DIMS", "total": 1, "byType": { "dimension": 1 } },
{ "name": "HOLES", "total": 1, "byType": { "circle": 1 } },
{ "name": "SLOT", "total": 2, "byType": { "line": 2 } }
],
"units": { "insUnits": 4, "name": "millimetres", "mmPerUnit": 1 },
"extents": {
"declared": { "min": [0, 0, 0], "max": [60, 40, 0] },
"computed": { "min": [0, -4, 0], "max": [33, 23, 0] }
},
"skipped": { "total": 1, "byType": { "INSERT": 1 }, "byLayer": { "BLOCKS": { "INSERT": 1 } } },
"source": {
"fileName": "slot.dxf", "bytes": 410, "format": "dxf", "dxfVersion": "AC1027",
"readerVersion": "0.3",
"sha256": "5c9c59756de36f6cddd4c027d7eb956005d7d19ca3935f0737cf51b6e2f6f9a2"
},
"warnings": ["1 block insert(s) are not expanded, so the geometry they place is missing from the entity list.", "1 entity record(s) were skipped; see `skipped`."]
}
(source.path and source.reader are also returned and are left out above for width.)
list_drawing_entities
List entities with identity, geometry and layer, filtered by type, layer or a rectangle on the sheet.
| Name | Type | Required | Description |
|---|---|---|---|
inputPath | string | yes | Absolute path of the DXF file. |
types | string[] | no | Any of line, circle, arc, ellipse, point, text, polyline, spline, dimension. |
layers | string[] | no | Layer names to keep. |
rect | number[4] | no | [xmin, ymin, xmax, ymax] on the sheet, in drawing units. |
rectMode | string | no | intersects (default) keeps entities whose bounds overlap rect; within keeps those wholly inside it. |
limit | integer | no | Page size, default 200, at most 5000. |
offset | integer | no | Entities to skip, default 0. |
Returns: total (matches before paging), offset, returned, truncated, unit (drawing units) and entities. Each entity has id, type, layer, color (the AutoCAD Color Index; 256 is by-layer), bounds and its geometry:
| Type | Fields |
|---|---|
line | start, end, length |
circle | center, radius |
arc | center, radius, startAngleDeg, endAngleDeg, sweepDeg, start, end, length (counter-clockwise from start to end) |
ellipse | center, majorAxis (relative to the center), ratio, startParam, endParam (radians) |
polyline | vertices, bulges (only when a segment is curved; a bulge is tan(θ/4) of the arc leaving that vertex), closed, length |
spline | degree, closed, periodic, rational, and as stored in the file: controlPoints, knots (the flat knot vector), weights (rational splines) and fitPoints (each left out when the file has none) |
text | at, height, rotationDeg, text |
point | at |
dimension | dimension, the record list_drawing_dimensions returns |
Bounds are tight for arcs (an arc of under half a turn does not contain its centre) and for the arcs of a bulged polyline. A spline’s box follows the curve (the curve flattened to within a ten-thousandth of the size of its control data, so the box can fall short of it by that much), not its control polygon, so a window that clears the curve does not touch it. An elliptical arc takes the bounds of its whole ellipse, and text is its insertion point, because the reader keeps no text extent.
Example: the two walls of a slot.
{ "inputPath": "/Users/me/drawings/slot.dxf", "layers": ["SLOT"] }
{
"total": 2, "offset": 0, "returned": 2, "truncated": false, "unit": "drawing units",
"source": { "fileName": "slot.dxf", "sha256": "5c9c59756de36f6c...", "readerVersion": "0.3" },
"entities": [
{ "id": "e0", "type": "line", "layer": "SLOT", "color": 256, "start": [0, 0, 0], "end": [0, 20, 0], "length": 20,
"bounds": { "min": [0, 0, 0], "max": [0, 20, 0] } },
{ "id": "e1", "type": "line", "layer": "SLOT", "color": 256, "start": [8, 0, 0], "end": [8, 20, 0], "length": 20,
"bounds": { "min": [8, 0, 0], "max": [8, 20, 0] } }
]
}
list_drawing_dimensions
Every DIMENSION in the drawing, with its value and the points that define it.
| Name | Type | Required | Description |
|---|---|---|---|
inputPath | string | yes | Absolute path of the DXF file. |
layers | string[] | no | Layer names to keep. |
kinds | string[] | no | Any of linear, aligned, angular, diameter, radius, angular3Point, ordinate. |
limit | integer | no | Page size, default 500, at most 5000. |
offset | integer | no | Dimensions to skip, default 0. |
Returns: total, offset, returned, truncated, units and dimensions. Each dimension has id (the same e<index> as in list_drawing_entities), kind, valueKind (length or angle), value (the stored measurement, DXF group 42, absent when the file omits it, which ezdxf notes is common), valueUnit, valueMm, valueFromPoints and valueFromPointsMm (see below), text (the user’s text override, absent when the dimension shows its own value: a text of exactly <> is DXF’s “show the measured value”, <> H7 is kept, and a single space, DXF’s “suppress the text”, is returned as a single space), textPosition, definitionPoints, layer and color.
- Each definition point carries its DXF
group(10, 13, 14, 15 or 16) and, where SwiftDXF documents it, arole: a linear or aligned dimension hasdimensionLine,extensionLine1OriginandextensionLine2Origin; a radius or diameter hasmeasuredFromandmeasuredTo. Other kinds return the groups with no role. valueMmis the value times millimetres per unit, for the length kinds only. An angular value is returned as stored: DXF does not declare its unit, so it has novalueMm.valueFromPointsis the length between the dimension’s own definition points: the two extension line origins of analigneddimension, the two ends of aradiusordiameter. It is the value when the file stores none, and a check when it does: a stale or overridden dimension disagrees with its geometry (a stored 7 over extension lines 8 apart). It is absent for the other kinds. Alineardimension’s measured direction is not modelled by the reader, so it has none.
Example
{ "inputPath": "/Users/me/drawings/slot.dxf" }
{
"total": 1, "offset": 0, "returned": 1, "truncated": false,
"units": { "insUnits": 4, "name": "millimetres", "mmPerUnit": 1 },
"dimensions": [
{
"id": "e3", "kind": "aligned", "valueKind": "length", "value": 8, "valueUnit": "drawing units", "valueMm": 8,
"valueFromPoints": 8, "valueFromPointsMm": 8,
"textPosition": [4, -4, 0], "layer": "DIMS", "color": 256,
"definitionPoints": [
{ "group": 10, "role": "dimensionLine", "point": [0, -4, 0] },
{ "group": 13, "role": "extensionLine1Origin", "point": [0, 0, 0] },
{ "group": 14, "role": "extensionLine2Origin", "point": [8, 0, 0] }
]
}
]
}
measure_drawing
Distance, angle or radius between named entities or points.
| Name | Type | Required | Description |
|---|---|---|---|
inputPath | string | yes | Absolute path of the DXF file. |
measure | string | yes | distance, angle or radius. |
a | string or number[] | for all | An entity id such as "e12", or a point [x, y] or [x, y, z]. |
b | string or number[] | distance, angle | The same. |
vertex | number[] | no | For angle between points: the apex. |
mmPerUnit | number | no | Millimetres per drawing unit, overriding the drawing’s declared units. |
distance: the minimum distance betweenaandb, each an entity or a point, with the nearest point on each (pointOnA,pointOnB). It is OCCT’s exact extrema over edges built from the entities, so the width of a slot is the true gap between its two walls, whether they are lines or arcs, and a circle, an arc and a polyline (curved segments included) are measured along their curves. A spline whose control data defines a curve is an exact NURBS edge, over its valid parameter domain (clamped or unclamped knots, rational or not). One that does not (fit points only, a knot vector of the wrong length or order, an empty domain, a degree above the 25 OCCT’s B-spline curves go to, or a closed spline whose ends do not meet, which SwiftDXF closes with a chord, as measured on the curve and not on the control points) is cut into chords within a tolerance of one ten-thousandth of the size of its control data, and the result says so innotes. An ellipse is cut into 48 chords, because OCCT’s ellipse edge takes no major-axis direction, and the result says so innotestoo. Atextentity has no geometry to measure: pass itsatpoint. Adimensionhas none either: measure the entities it dimensions, or pass its definition points fromlist_drawing_dimensionsas points.angle: twolineentities, orvertexwith two pointsaandb.valueis in degrees between 0 and 180 and runs between the lines’ drawn directions (start to end);acuteAngleDegis the angle between the lines as undirected lines, 0 to 90.radius: acircleorarcentity asa. Returns the radius and thediameter.
Returns: measure, a / b / vertex (what each end resolved to), value and unit (drawing units, or degrees for an angle), valueMm (absent for an angle and when the units are unknown), mmPerUnit, and notes.
Example: the slot width, measured between its two named walls rather than read off a picture.
{ "inputPath": "/Users/me/drawings/slot.dxf", "measure": "distance", "a": "e0", "b": "e1" }
{
"measure": "distance",
"a": { "kind": "entity", "id": "e0", "type": "line" },
"b": { "kind": "entity", "id": "e1", "type": "line" },
"value": 8, "unit": "drawing units", "valueMm": 8, "mmPerUnit": 1,
"pointOnA": [0, 0, 0], "pointOnB": [8, 0, 0],
"notes": [],
"source": { "fileName": "slot.dxf", "sha256": "5c9c59756de36f6c...", "readerVersion": "0.3" }
}
That is the stored value of the slot’s own dimension (list_drawing_dimensions returns 8 for it), so a stale or overridden dimension shows up as a mismatch.
Errors: a missing file, an unsupported format, an id that is not e<number> or is out of range, a text or dimension end for distance, and a non-line for angle each return isError with the reason.