autocad-dxf
Version:
A module which can be used to parse AutoCAD dxf files and to make programmatic and geometric operations on the AutoCAD drawing entities.
1,264 lines (1,215 loc) • 139 kB
JavaScript
const Distance = require("./Distance");
const Length = require("./Length");
const Intersection = require("./Intersection");
const Closest = require("./Closest");
const Triangulate = require("./Triangulate");
const BSpline = require("./BSpline");
const Area = require("./Area");
const Connected = require("./Connected");
const Crossing = require("./Crossing");
const Tangent = require("./Tangent");
const Draw = require("./Draw");
const Checkif = require("./Checkif");
const ErrorMessages = require("./ErrorMessages.json");
const DIMSTYLE_CODES = require("./DIMSTYLE_CODES.json");
const COLOR_CODES = require("./COLOR_CODES");
const KEYS = require("./KEYS");
const CODES = require("./CODES");
const HEADER_MAPS = require("./HEADER_MAPS");
const Entities = class {
constructor(data, tolerance) {
this.entities = [];
this.tables = {};
this.blocks = [];
this.tolerance = isNaN(tolerance) ? 0.0001 : tolerance;
this.KEYS = KEYS;
this.CODES = CODES;
if (data && typeof data == "string") {
this.processData(data.split("\n"));
}
}
getHeaders = (array, COUNT) => {
this.headers = {};
let currentKey = null;
let readableKey = null;
while (COUNT < array.length - 1) {
const code = array[COUNT].trim();
const value = array[COUNT + 1].trim();
if (code === "0" && value === "ENDSEC") {
return COUNT + 2;
} else if (code === "9") {
currentKey = value.replace('$', '');
readableKey = HEADER_MAPS.HEADER_KEYS[currentKey] || currentKey;
} else if (code === "10") {
this.headers[readableKey] = {x: parseFloat(value)};
} else if (code === "20") {
this.headers[readableKey].y = parseFloat(value);
} else if (code === "30") {
this.headers[readableKey].z = parseFloat(value);
} else if (currentKey) {
const readableValue = (HEADER_MAPS.HEADER_VALUES[currentKey] ? HEADER_MAPS.HEADER_VALUES[currentKey][value] : value) || value;
const val = (readableValue - 0);
this.headers[readableKey] = isNaN(val) ? readableValue : val;
}
COUNT += 2;
}
}
getClasses = (array, COUNT) => {
this.classes = [];
let currentClass = null;
while (COUNT < array.length - 1) {
const code = array[COUNT].trim();
const value = array[COUNT + 1].trim();
if (code === "0" && value === "ENDSEC") {
return COUNT + 2;
} else if (code === "0" && value === "CLASS") {
if (currentClass) this.classes.push(currentClass);
currentClass = {};
} else if (currentClass) {
if (code == "1") {
currentClass.name = value;
} else if (code == "2") {
currentClass["C++ class name"] = value;
} else if (code == "3") {
currentClass.application_name = value;
} else if (code == "90") {
const C_VALUES = {
"0": "No operations allowed (0)",
"1": "Erase allowed (0x1)",
"2": "Transform allowed (0x2)",
"4": "Color change allowed (0x4)",
"8": "Layer change allowed (0x8)",
"16": "Linetype change allowed (0x10)",
"32": "Linetype scale change allowed (0x20)",
"64": "Visibility change allowed (0x40)",
"128": "Cloning allowed (0x80)",
"256": "Lineweight change allowed (0x100)",
"512": "Plot Style Name change allowed (0x200)",
"895": "All operations except cloning allowed (0x37F)",
"1023": "All operations allowed (0x3FF)",
"1024": "Disables proxy warning dialog (0x400)",
"32768": "R13 format proxy (0x8000)"
};
if (value === "0") {
currentClass.proxy_capabilities = [C_VALUES[value]];
} else {
const result = [];
const flag = parseInt(value);
for (const key of Object.keys(C_VALUES)) {
if ((flag & key) !== 0) {
result.push(C_VALUES[key]);
}
}
currentClass.proxy_capabilities = result;
}
} else if (code == "91") {
currentClass.instance_count = parseInt(value);
} else if (code == "280") {
currentClass.was_proxy = value == "1" ? "Yes": "No";
} else if (code == "281") {
currentClass.is_entity = value == "1" ? "Yes": "No";
} else {
currentClass[code] = value;
}
}
COUNT += 2;
}
if (currentClass) this.classes.push(currentClass);
}
getObjects = (array, COUNT) => {
const objects = [];
let currentObjectData = null; // Stores the object being built
let currentObjectType = null;
let collecting = false;
// Temporary storage for dictionary entries or other list-like items
let currentDictionaryEntry = null;
let currentMlineStyleElement = null;
// --- Enhanced Object Type Mappings ---
// Common properties that apply to many objects.
// We can merge these into specific objects or handle them as a base.
const commonObjectProperties = {
"5": "id",
"330": "ref_ids", // Often the dictionary that owns this object
"100": "subclass" // e.g., "AcDbDictionary"
};
const objectTypeMappings = {
// --- Core Objects ---
"DICTIONARY": {
...commonObjectProperties,
"280": "hard_owner_flag", // Older flag
"281": "cloning_flag", // 0 = not clonable, 1 = clonable
// Codes 3 (name) and 350/360/340 (handle) are for entries, handled specially
},
"ACDBDICTIONARYWDFLT": { // Dictionary With Default
...commonObjectProperties,
"281": "cloning_flag",
"3": "entry_name", // For entries (special handling needed for multiple)
"340": "default_object_handle", // Handle to the default object
"350": "entry_object_handle" // For entries (special handling needed for multiple)
},
"DICTIONARYVAR": {
...commonObjectProperties,
"330": "owner_handle",
"280": "object_schema_number", // (0-255)
"1": "value"
},
"XRECORD": {
...commonObjectProperties,
"330": "owner_handle",
"280": "cloning_flag", // (1 = keep existing, 2 = use copy, etc.)
// XRECORDs can have almost any group code from 1-369.
// These will be collected into a 'data_pairs' array by default if not specifically mapped.
},
"ACDBPLACEHOLDER": { // Placeholder for objects whose type is unknown
...commonObjectProperties
},
"ACAD_PROXY_OBJECT": { // More specific proxy object
...commonObjectProperties,
"90": "proxy_object_class_id",
"91": "application_description",
"92": "original_class_dxf_name",
"93": "proxy_graphics_size",
// "310": "proxy_graphics_data", // Binary, handle carefully
// "311": ...
"70": "proxy_flags"
},
// --- Layout and Plotting ---
"LAYOUT": {
...commonObjectProperties,
"1": "layout_name",
"70": "layout_flags", // Bit-coded
"71": "tab_order",
"10": "limits_min_x", "20": "limits_min_y",
"11": "limits_max_x", "21": "limits_max_y",
"12": "insertion_base_x", "22": "insertion_base_y", "32": "insertion_base_z",
"14": "extents_min_x", "24": "extents_min_y", "34": "extents_min_z",
"15": "extents_max_x", "25": "extents_max_y", "35": "extents_max_z",
"146": "elevation",
"13": "ucs_origin_x", "23": "ucs_origin_y", "33": "ucs_origin_z",
"16": "ucs_x_axis_x", "26": "ucs_x_axis_y", "36": "ucs_x_axis_z",
"17": "ucs_y_axis_x", "27": "ucs_y_axis_y", "37": "ucs_y_axis_z",
"76": "ucs_orthographic_type", // 0=Not ortho, 1=Top, ..., 6=Right
"331": "associated_paperspace_block_record_handle", // Handle of the *Paper_Space## block
"345": "active_viewport_handle", // If this is the current layout
"346": "plot_settings_handle" // Handle to PLOTSETTINGS object
},
"PLOTSETTINGS": {
...commonObjectProperties,
"1": "plot_layout_name", // Name of layout these settings apply to
"2": "plot_configuration_name", // e.g., "DWG To PDF.pc3"
"4": "paper_size_name", // e.g., "ISO_A4_(210.00_x_297.00_MM)"
"6": "plot_view_name", // Name of a saved view to plot, or ""
"7": "plot_device_name", // e.g., "DWG to PDF" (if code 2 is empty)
"40": "paper_margin_left", "41": "paper_margin_bottom",
"42": "paper_margin_right", "43": "paper_margin_top",
"44": "paper_width", "45": "paper_height", // In millimeters
"46": "plot_origin_x", "47": "plot_origin_y", // In millimeters
"48": "plot_window_min_x", "49": "plot_window_min_y",
"50": "plot_window_max_x", "51": "plot_window_max_y",
"70": "plot_flags", // Bit-coded (e.g., plot transparency, plot object lineweights)
"72": "plot_paper_units", // 0=inches, 1=mm, 2=pixels
"73": "plot_rotation", // 0=0deg, 1=90deg, 2=180deg, 3=270deg
"74": "plot_type", // 0=Layout, 1=Extents, 2=Display, 3=View, 4=Window, 5=Limits (R14)
"75": "plot_style_sheet_name", // e.g., "acad.ctb" or "monochrome.stb"
"76": "standard_scale_type", // 0=Scaled to Fit, 1-16=standard scales, >16=custom
"77": "plot_scale_numerator", // Custom scale numerator (e.g., 1 for 1:100)
"78": "plot_scale_denominator", // Custom scale denominator (e.g., 100 for 1:100)
"147": "scale_factor", // If standard_scale_type is 0 (fit) or 1-16
"148": "paper_image_origin_x", "149": "paper_image_origin_y",
// ... many more plot settings exist
},
// --- Grouping and Style Objects ---
"GROUP": {
...commonObjectProperties,
"300": "description",
"70": "unnamed_flag", // 1 if unnamed, 0 if named
"71": "selectability_flag", // 1 if selectable, 0 if not
// Code 340 (entity handle) repeats, handled specially
},
"MLINESTYLE": {
...commonObjectProperties,
"2": "style_name",
"3": "description",
"70": "flags", // Bit-coded (e.g., fill, display miters, start/end caps)
"62": "fill_color_aci", // If fill is on
"51": "start_angle_degrees",
"52": "end_angle_degrees",
"71": "element_count",
// Element data (offset 49, color 62, linetype 6) repeats, handled specially
},
"VISUALSTYLE": {
...commonObjectProperties,
"2": "style_name", // Usually internal like "Wireframe", "Conceptual", "Realistic"
"3": "description",
"70": "type_flags", // e.g., internal, user-defined
"71": "face_lighting_model", // 0=Invisible, 1=Constant, 2=Phong, 3=Gooch
"72": "face_lighting_quality", // 0=No lighting, 1=Per-face, 2=Per-vertex
"73": "face_color_mode", // 0=No color, 1=Object, 2=Background, ...
"74": "face_modifiers", // Bit-coded (e.g., opacity, specular)
"62": "edge_color_aci",
"75": "edge_style_model", // 0=No edges, 1=Isolines, 2=Facet edges
// ... many more properties for edge styles, display settings, etc.
},
"MATERIAL": {
...commonObjectProperties,
"2": "material_name",
"3": "description",
"70": "ambient_color_method", // 0=Use current, 1=Override
"40": "ambient_color_factor", // if 70=1
"62": "ambient_color_aci", // if 70=1 (obsolete)
"420": "ambient_color_rgb", // True color, if 70=1
// ... similar sets for diffuse_color, specular_color, reflection_map, opacity_map etc.
},
"GEODATA": { // Geographic Data
...commonObjectProperties,
"90": "version",
"70": "coordinate_type", // 1=Local GIS, 2=Projected GIS, 3=Geographic Lat/Long
"10": "design_point_x", "20": "design_point_y", "30": "design_point_z", // WCS point
"11": "reference_point_x", "21": "reference_point_y", "31": "reference_point_z", // GIS point
"40": "horizontal_unit_scale", // Factor to convert drawing units to meters
"41": "vertical_unit_scale",
"210": "up_direction_x", "220": "up_direction_y", "230": "up_direction_z",
"2": "epsg_code", // or coordinate system definition string
"3": "coordinate_system_definition_xml",
"91": "observation_from_handle",
"92": "observation_to_handle",
"93": "scale_estimation_method",
// Mesh data (codes 94, 95, 12, 22, 13, 23) if it's a mesh type GeoData
},
"SORTENTSTABLE": { // Controls draw order of entities
...commonObjectProperties,
// Pairs of 331 (entity handle) and 5 (sort handle for that entity) repeat
},
"DATATABLE": {
...commonObjectProperties,
"2": "table_name",
"70": "number_of_columns",
"71": "number_of_rows",
// Column definitions (codes 280-289 per column for type, name, etc.)
// Cell data (codes based on type)
},
"SCALE": { // Annotation Scale
...commonObjectProperties,
"2": "scale_name", // e.g., "1:100"
"70": "flags", // e.g., is paper scale
"140": "paper_units",
"141": "drawing_units",
"142": "is_scale_temporary", // (Obsolete)
},
// Add more object types here as needed...
"TABLESTYLE": { /* ... */ },
"DIMSTYLE": { /* ... Note: DIMSTYLE is a table, not an OBJECTS section object usually, but custom ones might be */ },
"FIELD": { /* ... */ },
"FIELDLIST": { /* ... */ },
"IDBUFFER": { /* ... */ },
"LAYER_FILTER": { /* ... */ },
"SPATIAL_FILTER": { /* ... */ },
"LIGHTLIST": { /* ... */ },
"RENDERSETTINGS": { /* ... (various types like RENDERGLOBAL, MENTALRAYRENDERSETTINGS) */ },
"SUN": { /* ... */ },
"VBA_PROJECT": { /* ... */ },
};
// Helper to safely add to a property, converting to array if it becomes a list
const _setProperty = (obj, key, value, isKnownList = false) => {
if (isKnownList) {
if (!obj[key]) {
obj[key] = [];
}
obj[key].push(value);
} else {
// This simplistic approach assumes if a key is repeated, it was meant to be a list.
// More robustly, the objectTypeMappings should indicate which keys are lists.
// For now, we'll handle known lists explicitly (DICTIONARY, GROUP, MLINESTYLE)
// and others will overwrite or use the specific logic below.
obj[key] = value;
}
};
while (COUNT < array.length - 1) {
const code = array[COUNT].trim();
const rawValue = array[COUNT + 1].trim(); // Keep raw value for strings
const parsedValue = /^\d+$/.test(rawValue) ? parseFloat(rawValue) : rawValue;
if (code === "0") {
if (rawValue === "ENDSEC") {
if (currentObjectData) objects.push(currentObjectData);
currentObjectData = null; // Reset
break;
}
if (currentObjectData) objects.push(currentObjectData);
currentObjectType = rawValue;
currentObjectData = { otype: currentObjectType }; // Basic object initialization
collecting = true;
// Reset temporary holders for list-like items
currentDictionaryEntry = null;
currentMlineStyleElement = null;
// Initialize lists for object types that have them
if (currentObjectType === "DICTIONARY" || currentObjectType === "ACDBDICTIONARYWDFLT") {
currentObjectData.entries = [];
} else if (currentObjectType === "GROUP") {
currentObjectData.entity_handles = [];
} else if (currentObjectType === "MLINESTYLE") {
currentObjectData.elements = [];
} else if (currentObjectType === "XRECORD") {
currentObjectData.data_pairs = [];
} else if (currentObjectType === "SORTENTSTABLE") {
currentObjectData.sort_items = [];
}
} else if (collecting && currentObjectData) {
const keyMap = objectTypeMappings[currentObjectType] || {};
const mappedKey = keyMap[code] || commonObjectProperties[code];
// --- Special Handling for Specific Object Types & Codes ---
if (currentObjectType === "DICTIONARY" || currentObjectType === "ACDBDICTIONARYWDFLT") {
if (code === "3") { // Entry name
currentDictionaryEntry = { name: parsedValue };
} else if (code === "350" || code === "360" || code === "340") { // Entry handle
if (currentDictionaryEntry) {
currentDictionaryEntry.handle = parsedValue;
currentObjectData.entries.push(currentDictionaryEntry);
currentDictionaryEntry = null;
} else { // Handle without preceding name (e.g. for ACDBDICTIONARYWDFLT default_object_handle if 340)
if (code === "340" && currentObjectType === "ACDBDICTIONARYWDFLT" && !keyMap[code] ){ // Assuming 340 is default if not mapped
currentObjectData.default_object_handle = parsedValue;
} else {
// Orphaned handle, or part of a non-entry property
if (mappedKey) {
currentObjectData[mappedKey] = parsedValue;
} else {
currentObjectData[`raw_${code}`] = parsedValue;
}
}
}
} else if (mappedKey) {
currentObjectData[mappedKey] = parsedValue;
} else {
currentObjectData[`raw_${code}`] = parsedValue;
}
} else if (currentObjectType === "GROUP") {
if (code === "340") { // Entity handle in the group
currentObjectData.entity_handles.push(parsedValue);
} else if (mappedKey) {
currentObjectData[mappedKey] = parsedValue;
} else {
currentObjectData[`raw_${code}`] = parsedValue;
}
} else if (currentObjectType === "MLINESTYLE") {
if (code === "49") { // Element offset (starts a new element definition)
currentMlineStyleElement = { offset: parsedValue };
} else if (code === "62" && currentMlineStyleElement) { // Element color
currentMlineStyleElement.color_aci = parsedValue;
} else if (code === "6" && currentMlineStyleElement) { // Element linetype
currentMlineStyleElement.linetype = parsedValue;
currentObjectData.elements.push(currentMlineStyleElement);
currentMlineStyleElement = null; // Ready for next element
} else if (mappedKey) { // For other MLINESTYLE properties (name, flags, etc.)
currentObjectData[mappedKey] = parsedValue;
} else {
currentObjectData[`raw_${code}`] = parsedValue;
}
} else if (currentObjectType === "XRECORD") {
// For XRECORD, collect all non-common, non-mapped codes as data pairs
if (mappedKey) { // handle, owner, subclass, cloning_flag
currentObjectData[mappedKey] = parsedValue;
} else {
currentObjectData.data_pairs.push({ code: parseInt(code), value: parsedValue });
}
} else if (currentObjectType === "SORTENTSTABLE") {
if (code === "331") { // Entity handle for sort item
currentObjectData.current_sort_entity_handle = parsedValue;
} else if (code === "5" && currentObjectData.current_sort_entity_handle) { // Sort handle for that entity
currentObjectData.sort_items.push({
entity_handle: currentObjectData.current_sort_entity_handle,
sort_handle: parsedValue // This is the actual entity handle again, sort order is by position
});
delete currentObjectData.current_sort_entity_handle;
} else if (mappedKey) {
currentObjectData[mappedKey] = parsedValue;
} else {
currentObjectData[`raw_${code}`] = parsedValue;
}
}
// --- General Case ---
else if (mappedKey) {
// You might want to identify specific keys that are lists here too.
// For example, LAYOUT's 331 (viewport_handle) can appear multiple times
// if it's a layout with multiple viewports (though typically one main one).
// For now, simple assignment.
_setProperty(currentObjectData, mappedKey, parsedValue, false); // `false` unless keyMap indicates it's a list
} else {
_setProperty(currentObjectData, `raw_${code}`, parsedValue, false);
}
}
COUNT += 2;
}
// Push the last collected object if any
if (currentObjectData) {
objects.push(currentObjectData);
}
this.objects = objects;
return COUNT;
}
/**
* Parses the THUMBNAILIMAGE section from a DXF data array.
* Assumes the main parser has already consumed "0", "SECTION", "2", "THUMBNAILIMAGE".
* This function should be called when COUNT points to the group code 90.
*
* @param {string[]} array - The flat array of DXF group codes and values.
* @param {number} COUNT - The starting index in the array (should point to group code 90).
* @returns {number} The updated COUNT index, pointing to the start of the next section's "0" code.
*/
getThumbnails = (array, COUNT) => {
let thumbnailHexData = ""; // To store the concatenated hexadecimal image data
let declaredSizeInBytes = 0; // Size indicated by group code 90
let inThumbnailData = true; // Flag to control the loop within the thumbnail data
// Loop while we are within the bounds of the array and expect thumbnail data
while (COUNT < array.length - 1 && inThumbnailData) {
const code = array[COUNT].trim();
const value = array[COUNT + 1].trim();
switch (code) {
case "90": // Specifies the size of the thumbnail image in bytes
declaredSizeInBytes = parseInt(value, 10);
COUNT += 2; // Consume the code and value
break;
case "310": // Contains a chunk of the hexadecimal image data
thumbnailHexData += value; // Concatenate in case the data is split
COUNT += 2; // Consume the code and value
break;
case "0":
// This "0" signifies the end of the THUMBNAILIMAGE data
// and the beginning of a new SECTION (e.g., "0", "SECTION").
inThumbnailData = false;
// Do NOT increment COUNT here; the "0" code belongs to the next section
// and will be processed by the main parsing loop.
break;
default:
// Encountering an unexpected group code within the thumbnail data block.
// This could indicate a malformed DXF or the end of the relevant data.
console.warn(`Unexpected group code ${code} in THUMBNAILIMAGE section. Stopping thumbnail parse.`);
inThumbnailData = false;
// Do NOT increment COUNT here; this unexpected code needs to be handled
// by the main parser or signals an issue.
break;
}
}
// Store the parsed thumbnail information on the context object (`this`)
if (thumbnailHexData.length > 0) {
this.thumbnailImage = {
declared_size_bytes: declaredSizeInBytes,
hex_data: thumbnailHexData,
// Optional: Calculate the actual number of bytes from the hex string length
// (each byte is represented by two hex characters)
actual_data_bytes: thumbnailHexData.length / 2
};
} else {
this.thumbnailImage = null; // No thumbnail data found or parsed
}
return COUNT; // Return the COUNT, now pointing to the "0" of the next section
}
getDimStyleParams = (code, value, key) => {
let actualValue;
if (code == "148" || code == "171" || code == "179" || code == "340") {
actualValue = value;
} else if (code == "371" || code == "372") {
if (value == "-3") {
actualValue = "Standard";
} else if (value == "-2") {
actualValue = "ByLayer";
} else if (value == "-1") {
actualValue = "ByBlock";
} else {
actualValue = `${(parseFloat(value)/100)}mm`;
}
} else if (code == "280" || code == "77" || code == "275" || code == "270" || code == "277" || code == "279" || code == "283" || code == "289") {
actualValue = DIMSTYLE_CODES[key][value];
} else if (code == "75" || code == "76" || code == "281" || code == "282") {
actualValue = value == "1" ? "Suppressed" : "Not suppressed";
} else {
let temp = /^[+-]?(\d+(\.\d*)?|\.\d+)$/.test(value) ? parseFloat(value) : value;
//if (isNaN(temp)) temp = value;
if (DIMSTYLE_CODES.booleans.indexOf(code) != -1) {
temp = (temp == 1) ? "On" : "Off";
}
actualValue = temp;
}
return actualValue;
}
getTables = (array, COUNT) => {
let ttype, json;
while (COUNT < array.length - 1) {
const code = array[COUNT].trim();
const value = array[COUNT + 1].trim();
if (code == "0" && value == "TABLE") {
ttype = array[COUNT + 3].trim();
this.tables[ttype] = [];
} else if (code == "0" && value == "ENDSEC") {
return COUNT + 2;
}
if (ttype == "APPID") {
if (code == "100" && value == "AcDbRegAppTableRecord") {
if (json) this.tables.APPID.push(json);
json = {};
} else if (json && code == "0" && value == "ENDTAB") {
this.tables.APPID.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
}
} else if (ttype == "BLOCK_RECORD") {
if (code == "100" && value == "AcDbBlockTableRecord") {
if (json) this.tables.BLOCK_RECORD.push(json);
json = {};
} else if (json && code == "0" && value == "ENDTAB") {
if (json) this.tables.BLOCK_RECORD.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
} else if (json && code == "331") {
if (!json.ref_id) json.ref_id = [];
json.ref_id.push(value);
}
} else if (ttype == "DIMSTYLE") {
if (code == "100" && value == "AcDbDimStyleTableRecord") {
if (json) this.tables.DIMSTYLE.push(json);
json = {};
} else if (json && code == "0" && value == "ENDTAB") {
this.tables.DIMSTYLE.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
} else if (json && DIMSTYLE_CODES[code]) {
if (code == "78" || code == "286") {
if (value == "0") {
json.suppress_zero_inches = "No";
json.suppress_zero_feet = "Yes";
} else if (value == "1") {
json.suppress_zero_inches = "No";
json.suppress_zero_feet = "No";
} else if (value == "2") {
json.suppress_zero_inches = "Yes";
json.suppress_zero_feet = "No";
} else if (value == "3") {
json.suppress_zero_inches = "No";
json.suppress_zero_feet = "Yes";
}
} else if (code == "284") {
if (value == "0") {
json.alt_suppress_leading_zeros = "No";
json.alt_suppress_trailing_zeros = "No";
} else if (value == "1") {
json.alt_suppress_leading_zeros = "Yes";
json.alt_suppress_trailing_zeros = "No";
} else if (value == "2") {
json.alt_suppress_leading_zeros = "No";
json.alt_suppress_trailing_zeros = "Yes";
} else if (value == "3") {
json.alt_suppress_leading_zeros = "Yes";
json.alt_suppress_trailing_zeros = "Yes";
}
} else if (code == "79" || code == "285") {
if (value == "0") {
json.suppress_leading_zeros = "No";
json.suppress_trailing_zeros = "No";
} else if (value == "1") {
json.suppress_leading_zeros = "Yes";
json.suppress_trailing_zeros = "No";
} else if (value == "2") {
json.suppress_leading_zeros = "No";
json.suppress_trailing_zeros = "Yes";
} else if (value == "3") {
json.suppress_leading_zeros = "Yes";
json.suppress_trailing_zeros = "Yes";
}
} else {
const key = DIMSTYLE_CODES[code]
const actualValue = this.getDimStyleParams(code, value, key);
json[key] = actualValue;
}
}
} else if (ttype == "LAYER") {
if (code == "100" && value == "AcDbLayerTableRecord") {
if (json) this.tables.LAYER.push(json);
json = {};
} else if (json && code == "0" && value == "ENDTAB") {
this.tables.LAYER.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
} else if (json && code == "70") {
if (value == "0") {
json.status = "Thawed";
} else if (value == "1") {
json.status = "Frozen";
} else if (value == "2") {
json.status = "Frozen by default";
} else if (value == "4") {
json.status = "Locked";
}
} else if (json && code == "62") {
json.color_number = value;
} else if (json && code == "6") {
json.line_type = value;
}
} else if (ttype == "LTYPE") {
if (code == "100" && value == "AcDbLinetypeTableRecord") {
if (json) this.tables.LTYPE.push(json);
json = {};
} else if (json && code == "0" && value == "ENDTAB") {
this.tables.LTYPE.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
} else if (json && code == "3") {
json.description = value;
} else if (json && code == "9") {
if (!json.embedded_texts) json.embedded_texts = [];
json.embedded_texts.push(value);
} else if (json && code == "40") {
json.total_pattern_length = parseInt(value);
} else if (json && code == "44") {
if (!json.x_offsets) json.x_offsets = [];
json.x_offsets.push(parseFloat(value));
} else if (json && code == "45") {
if (!json.y_offsets) json.y_offsets = [];
json.y_offsets.push(parseFloat(value));
} else if (json && code == "46") {
if (!json.scale_value) json.scale_value = [];
json.scale_value.push(parseFloat(value));
} else if (json && code == "49") {
if (!json.pattern_lengths) json.pattern_lengths = [];
json.pattern_lengths.push(parseFloat(value));
} else if (json && code == "50") {
if (!json.embedded_element_rotations) json.embedded_element_rotations = [];
json.embedded_element_rotations.push(parseFloat(value));
} else if (json && code == "73") {
json.number_of_elements = parseInt(value);
} else if (json && code == "74") {
if (value == "0") {
json.embedded_element_type = "None";
} else if (value == "1") {
json.embedded_element_rotation_type = "Absolute";
} else if (value == "2") {
json.embedded_element_type = "Text";
} else if (value == "4") {
json.embedded_element_type = "Shape";
}
}
} else if (ttype == "STYLE") {
if (code == "100" && value == "AcDbTextStyleTableRecord") {
if (json) this.tables.STYLE.push(json);
json = {text_height: 0};
} else if (json && code == "0" && value == "ENDTAB") {
this.tables.STYLE.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
} else if (json && code == "3") {
json.font_file_name = value;
} else if (json && code == "5") {
json.font_style_handle = value;
} else if (json && code == "40") {
json.text_height = parseFloat(value);
} else if (json && code == "41") {
json.width_factor = parseFloat(value);
} else if (json && code == "50") {
json.oblique_angle = parseFloat(value);
} else if (json && code == "70") {
if (value == "1") {
json.type = "Shape";
} else if (value == "4") {
json.type = "Vertical text";
}
} else if (json && code == "71") {
if (value == "2") {
json.text_type = "Backward (mirrored in X)";
} else if (value == "4") {
json.text_type = "Upside down (mirrored in Y)";
}
} else if (json && code == "1000") {
json.font_name = value;
} else if (json && code == "1071") {
json.font_type = value;
}
} else if (ttype == "UCS") {
if (code == "100" && value == "AcDbUCSTableRecord") {
if (json) this.tables.UCS.push(json);
json = {};
} else if (json && code == "0" && value == "ENDTAB") {
this.tables.UCS.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
} else if (json && code == "10") {
json.origin = {x : parseFloat(value)};
} else if (json && code == "11") {
json.x_axis_direction = {x : parseFloat(value)};
} else if (json && code == "12") {
json.y_axis_direction = {x: parseFloat(value)};
} else if (json && code == "13") {
json.orthographic_origin = {x: parseFloat(value)};
} else if (json && code == "20") {
json.origin.y = parseFloat(value);
} else if (json && code == "21") {
json.x_axis_direction.y = parseFloat(value);
} else if (json && code == "22") {
json.y_axis_direction.y = parseFloat(value);
} else if (json && code == "23") {
json.orthographic_origin.y = parseFloat(value);
} else if (json && code == "30") {
json.origin.z = parseFloat(value);
} else if (json && code == "31") {
json.x_axis_direction.z = parseFloat(value);
} else if (json && code == "32") {
json.y_axis_direction.z = parseFloat(value);
} else if (json && code == "33") {
json.orthographic_origin.z = parseFloat(value);
} else if (json && code == "71") {
if (value == "1") {
json.orthographic_type = "Top";
} else if (value == "2") {
json.orthographic_type = "Bottom";
} else if (value == "3") {
json.orthographic_type = "Front";
} else if (value == "4") {
json.orthographic_type = "Back";
} else if (value == "5") {
json.orthographic_type = "Left";
} else if (value == "6") {
json.orthographic_type = "Right";
}
} else if (json && code == "146") {
json.elevation = parseFloat(value);
}
} else if (ttype == "VIEW") {
if (code == "100" && value == "AcDbViewTableRecord") {
if (json) this.tables.VIEW.push(json);
json = {hasUCS: false};
} else if (json && code == "0" && value == "ENDTAB") {
this.tables.VIEW.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
} else if (json && code == "10") {
json.center = {x : parseFloat(value)};
} else if (json && code == "11") {
json.direction = {x : parseFloat(value)};
} else if (json && code == "12") {
json.target_point = {x: parseFloat(value)};
} else if (json && code == "20") {
json.center.y = parseFloat(value);
} else if (json && code == "21") {
json.direction.y = parseFloat(value);
} else if (json && code == "22") {
json.target_point.y = parseFloat(value);
} else if (json && code == "31") {
json.direction.z = parseFloat(value);
} else if (json && code == "32") {
json.target_point.z = parseFloat(value);
} else if (json && code == "40") {
json.height = parseFloat(value);
} else if (json && code == "41") {
json.width = parseFloat(value);
} else if (json && code == "42") {
json.lens_angle = parseFloat(value);
} else if (json && code == "50") {
json.twist_angle = parseFloat(value);
} else if (json && code == "70") {
if (value == "1") {
json.type = "Paper space view";
}
} else if (json && code == "72") {
if (value == "1") {
json.hasUCS = true;
}
} else if (json && code == "79") {
if (value == "0") {
json.orthographic_type = "N/A";
} else if (value == "1") {
json.orthographic_type = "Top";
} else if (value == "2") {
json.orthographic_type = "Bottom";
} else if (value == "3") {
json.orthographic_type = "Front";
} else if (value == "4") {
json.orthographic_type = "Back";
} else if (value == "5") {
json.orthographic_type = "Left";
} else if (value == "6") {
json.orthographic_type = "Right";
}
} else if (json && code == "110") {
json.origin = {x: parseFloat(value)};
} else if (json && code == "111") {
json.ucs_x = {x: parseFloat(value)};
} else if (json && code == "112") {
json.ucs_y = {x: parseFloat(value)};
} else if (json && code == "120") {
json.origin.y = parseFloat(value);
} else if (json && code == "121") {
json.ucs_x.y = parseFloat(value);
} else if (json && code == "122") {
json.ucs_y.y = parseFloat(value);
} else if (json && code == "130") {
json.origin.z = parseFloat(value);
} else if (json && code == "131") {
json.ucs_x.z = parseFloat(value);
} else if (json && code == "132") {
json.ucs_y.z = parseFloat(value);
} else if (json && code == "146") {
json.elevation = parseFloat(value);
}
} else if (ttype == "VPORT") {
if (code == "100" && value == "AcDbViewportTableRecord") {
if (json) this.tables.VPORT.push(json);
json = {};
} else if (json && code == "0" && value == "ENDTAB") {
this.tables.VPORT.push(json);
json = null;
} else if (json && code == "2") {
json.name = value;
} else if (json && code == "10") {
json.lower_left_corner = {x : parseFloat(value)};
} else if (json && code == "11") {
json.upper_right_corner = {x : parseFloat(value)};
} else if (json && code == "12") {
json.center = {x: parseFloat(value)};
} else if (json && code == "13") {
json.snap_base_point = {x: parseFloat(value)};
} else if (json && code == "14") {
json.snap_spacing = {x: parseFloat(value)};
} else if (json && code == "15") {
json.grid_spacing = {x: parseFloat(value)};
} else if (json && code == "16") {
json.view_direction = {x: parseFloat(value)};
} else if (json && code == "17") {
json.target_point = {x: parseFloat(value)};
} else if (json && code == "20") {
json.lower_left_corner.y = parseFloat(value);
} else if (json && code == "21") {
json.upper_right_corner.y = parseFloat(value);
} else if (json && code == "22") {
json.center.y = parseFloat(value);
} else if (json && code == "23") {
json.snap_base_point.y = parseFloat(value);
} else if (json && code == "24") {
json.snap_spacing.y = parseFloat(value);
} else if (json && code == "25") {
json.grid_spacing.y = parseFloat(value);
} else if (json && code == "26") {
json.view_direction.y = parseFloat(value);
} else if (json && code == "27") {
json.target_point.y = parseFloat(value);
} else if (json && code == "36") {
json.view_direction.z = parseFloat(value);
} else if (json && code == "37") {
json.target_point.z = parseFloat(value);
} else if (json && code == "45") {
json.height = parseFloat(value);
} else if (json && code == "42") {
json.lens_length = parseFloat(value);
} else if (json && code == "50") {
json.snap_rotation_angle = parseFloat(value);
} else if (json && code == "51") {
json.twist_angle = parseFloat(value);
} else if (json && code == "72") {
json.circle_sides = value;
} else if (json && code == "79") {
if (value == "0") {
json.orthographic_type = "N/A";
} else if (value == "1") {
json.orthographic_type = "Top";
} else if (value == "2") {
json.orthographic_type = "Bottom";
} else if (value == "3") {
json.orthographic_type = "Front";
} else if (value == "4") {
json.orthographic_type = "Back";
} else if (value == "5") {
json.orthographic_type = "Left";
} else if (value == "6") {
json.orthographic_type = "Right";
}
} else if (json && code == "110") {
json.origin = {x: parseFloat(value)};
} else if (json && code == "111") {
json.ucs_x = {x: parseFloat(value)};
} else if (json && code == "112") {
json.ucs_y = {x: parseFloat(value)};
} else if (json && code == "120") {
json.origin.y = parseFloat(value);
} else if (json && code == "121") {
json.ucs_x.y = parseFloat(value);
} else if (json && code == "122") {
json.ucs_y.y = parseFloat(value);
} else if (json && code == "130") {
json.origin.z = parseFloat(value);
} else if (json && code == "131") {
json.ucs_x.z = parseFloat(value);
} else if (json && code == "132") {
json.ucs_y.z = parseFloat(value);
} else if (json && code == "146") {
json.elevation = parseFloat(value);
}
}
COUNT = COUNT + 2;
}
}
getBlocks = (array, COUNT) => {
let json, json2, blockBegan = false, entityStarted = false;
while (COUNT < array.length - 1) {
const code = array[COUNT].trim();
const value = array[COUNT + 1].trim();
if (code == "0" && value == "BLOCK") {
json = {};
} else if (code == "0" && value == "ENDSEC") {
this.blocks.push(json);
return COUNT + 2;
} else if (code == "0" && value == "ENDBLK") {
if (json.entities && json2) json.entities.push(json2);
this.blocks.push(json);
blockBegan = false;
} else if (code == "100" && value == "AcDbBlockBegin") {
blockBegan = true;
entityStarted = false;
}
if (blockBegan && code == "0") {
if (!json.entities) json.entities = [];
if (json2) json.entities.push(json2);
json2 = {etype: value};
entityStarted = true;
} else if (blockBegan && code == "2") {
json.name = value;
} else if (blockBegan && code == "4") {
json.description = value;
} else if (json && json.id === undefined && !blockBegan && code == "5") {
json.id = value;
} else if (!blockBegan && code == "8") {
json.layer = value;
} else if (!blockBegan && code == "330") {
if (!json.ref_id) json.ref_id = [];
if (json.ref_id.indexOf(value) == -1) json.ref_id.push(value);
} else if (!entityStarted && code == "10") {
json.base_point = { x : parseFloat(value) };
} else if (!entityStarted && code == "20") {
json.base_point.y = parseFloat(value);
} else if (!entityStarted && code == "30") {
json.base_point.z = parseFloat(value);
} else if (blockBegan && json2) {
this.insertEntity(code, value, json2);
}
COUNT = COUNT + 2;
}
}
getEntities = (array, COUNT) => {
let json, json2;
while (COUNT < array.length - 1) {
const code = array[COUNT].trim();
const value = array[COUNT + 1].trim();
if (code == "0" && value == "ENDSEC" && json.etype != "SEQEND") {
if (json.subclass == "AcDbDimension" && (json.specific_type == 'AcDbRotatedDimension' || json.specific_type == 'AcDbAlignedDimension')) {
this.getDimLineCoordinates(json);
}
this.entities.push(json);
return COUNT + 2;
}
if (value == "DSTYLE") {
let ds_code = code, ds_value = value;
while (ds_value != "}" && ds_code != "0") {
COUNT = COUNT + 2;
ds_code = array[COUNT].trim();
ds_value = array[COUNT + 1].trim();
if (DIMSTYLE_CODES[ds_value] !== undefined) {
const temp = array[COUNT + 3].trim();
const key = DIMSTYLE_CODES[ds_value];
const actualValue = this.getDimStyleParams(ds_value, temp, key);
json[key] = actualValue;
COUNT = COUNT + 2;
}
}
}
if (code == "0" && (!json || (json.subclass && json.subclass != "AcDb3dPolyline" && json.etype != "SEQEND"))) {
if (json) {
if (json.subclass == "AcDbDimension" && (json.specific_type == 'AcDbRotatedDimension' || json.specific_type == 'AcDbAlignedDimension')) {
this.getDimLineCoordinates(json);
}
this.entities.push(json);
}
json = {};
this.insertEntity(code, value, json);
} else if (code == "0" && json && json.subclass == "AcDb3dPolyline" && value == "SEQEND") {
if (json2 && json.vertices) json.vertices.push(json2);
if (json) this.entities.push(json);
json = {};
json2 = undefined;
while (array[COUNT + 2].trim() != "0") {
COUNT = COUNT + 2;
}
} else if (code == "0" && json && json.subclass == "AcDb3dPolyline") {
if (!json.vertices) json.vertices = [];
if (json2) json.vertices.push(json2);
json2 = {};
this.insertEntity(code, value, json2);
} else if (json2) {
this.insertEntity(code, value, json2);
} else {
this.insertEntity(code, value, json);
}
COUNT = COUNT + 2;
}
}
insertEntity = (code, value, json) => {
if (json && !json.etype && code == "0") {
json.etype = value;
json.line_type = "ByLayer";
json.color = "ByLayer";
} else if (code == "1") {
if (json.subclass == "AcDbText" || json.subclass == "AcDbMText") {
const regex = /(\\P|\\L|\{|\}|\\*\\*a\d+;|\\H\d+\.?\d*x;|\+\/\-|%%u|\\Fromanc\||\\f.*p\d+;|t\d+;|c\d+;|\\fFutura Md BT\||\\Fsimplex\||\\fitalic.*c.*\d+;|scale.*\d+:\d+)/gim;
json.raw_text = value;
json.text = value.replace(regex, "");
json.style = "STANDARD";
json.rotation = 0;
} else if (json.subclass == "AcDbDimension") {
const regex = /(\\P|\\L|\{|\}|\\*\\*a\d+;|\\H\d+\.?\d*x;|\+\/\-|%%u|\\Fromanc\||\\f.*p\d+;|t\d+;|c\d+;|\\fFutura Md BT\||\\Fsimplex\||\\fitalic.*c.*\d+;|scale.*\d+:\d+)/gim;
json.text_override = value.replace(regex, "");
} else if (json.subclass == "AcDbModelerGeometry") {
if (!json.proprietary_data) json.proprietary_data = [];
json.proprietary_data.push(value);
} else if (json.subclass == "AcDbFcf") {
json.visual_representation = value;
}
} else if (code == "2") {
if (json.subclass == "AcDbShape" || json.subclass == "AcDbMline" || json.subclass == "AcDbHatch") {
json.style_name = value;
} else if (json.subclass == "AcDbBlockReference") {
json.block_name = value;
}
} else if (code == "3") {
if (json.subclass == "AcDbDimension" || json.subclass == "AcDbLeader" || json.subclass == "AcDbFcf") {
json.dimension_style = value;
} else if (json.subclass == "AcDbModelerGeometry") {
const temp = json.proprietary_data[json.proprietary_data.length - 1];
json.proprietary_data[json.proprietary_data.length - 1] = temp + value;
}
} else if (code == "5") {
json.id = value;
} else if (code == "6") {
json.line_type = value;
} else if (code == "7") {
if (json.subclass == "AcDbMText") {
json.style = value;
} else if (json.subclass == "AcDbDimension") {
json.text_style_name = value;
} else if (json.etype == "ACAD_TABLE" && json.subclass == "AcDbBlockReference") {
if (json.cells && json.cells[json.cells.length - 1]) {
json.cells[json.cells.length - 1].text_style = value;
} else {
json.text_style = value;
}
}
} else if (code == "8") {
json.layer = value;
} else if (code == "10") {
if (json.subclass == "AcDbLine" || json.subclass == "AcDbRay" || json.subclass == "AcDbMline") {
json.start_x = parseFloat(value);
} else if (json.subclass == "AcDbPolyline" || json.subclass == "AcDbLeader") {
if (!json.vertices) json.vertices = [];
json.vertices.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbSpline") {
if (!json.control_points) json.control_points = [];
json.control_points.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbTrace" || json.subclass == "AcDbFace") {
if (!json.corners) json.corners = [];
json.corners.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbHatch") {
let edge;
try {
edge = json.boundary_paths[json.boundary_paths.length - 1];
} catch(e) {}
if (json.elevation_point === undefined) {
json.elevation_point = {x: parseFloat(value)};
} else if (edge && json.number_of_seed_points === undefined) {
if (edge.path_type.indexOf("Polyline") != -1) {
if (!edge.vertices) edge.vertices = [{}];
edge.vertices[edge.vertices.length - 1].x = parseFloat(value);
} else if (edge.edge_type == "Line" && edge.start_x === undefined) {
edge.start_x = parseFloat(value);
} else if (edge.edge_type == "Circular arc") {
edge.center_x = parseFloat(value);
} else if (edge.edge_type == "Elliptic arc") {
edge.center_x = parseFloat(value);
} else if (edge.edge_type == "Spline") {
if (!edge.control_points) edge.control_points = [];
edge.control_points.push({x: parseFloat(value)});
}
} else {
if (!json.seed_points) json.seed_points = [];
json.seed_points.push({x: parseFloat(value)});
}
} else if (json.subclass == "AcDbHelix") {
if (!json.axis_base_point) json.axis_base_point = [];
json.axis_base_point.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbRasterImage") {
if (!json.insertion_point) json.insertion_point = [];
json.insertion_point.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbXline") {
if (!json.first_point) json.first_point = {};
json.first_point.x = parseFloat(value);
} else if (json.subclass == "AcDbFcf") {
if (!json.insertion_point) json.insertion_point = {};
json.insertion_point.x = parseFloat(value);
} else {
json.x = parseFloat(value);
}
} else if (code == "11") {
if (json.subclass == "AcDbLine") {
json.end_x = parseFloat(value);
} else if (json.subclass == "AcDbSpline") {
if (!json.fit_points) json.fit_points = [];
json.fit_points.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbEllipse") {
json.major_end_dx = parseFloat(value);
} else if (json.subclass == "AcDbDimension") {
json.x_text = parseFloat(value);
} else if (json.subclass == "AcDbTrace" || json.subclass == "AcDbFace") {
if (!json.corners) json.corners = [];
json.corners.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbRay") {
json.unit_direction_x = parseFloat(value);
} else if (json.subclass == "AcDbHelix") {
if (!json.start_point) json.start_point = [];
json.start_point.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbMline") {
if (!json.vertices) json.vertices = [];
json.vertices.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbXline") {
if (!json.unit_direction_vector) json.unit_direction_vector = {};
json.unit_direction_vector.x = parseFloat(value);
} else if (json.subclass == "AcDbRasterImage") {
if (!json.u_vector) json.u_vector = [];
json.u_vector.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbModelerGeometry" && json.specific_type == "AcDbRevolvedSurface") {
if (!json.axis_vector) json.axis_vector = [];
json.axis_vector.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbModelerGeometry" && (json.specific_type == "AcDbExtrudedSurface" || json.specific_type == "AcDbSweptSurface")) {
if (!json.ref_vector) json.ref_vector = [];
json.ref_vector.push({x: parseFloat(value)});
} else if (json.subclass == "AcDbBlockReference" && json.specific_type == "AcDbTable") {
json.direction_vector = {x: parseFloat(value)};
} else if (json.subclass == "AcDbFcf") {
if (!json.x_axis_direction) json.x_axis_direction = {};
json.x_axis_direction.x = parseFloat(value);
} else if (json.subclass == "AcDbHatch") {
let edge;
try {
edge = json.boundary_paths[json.boundary_paths.length - 1];
} catch(e) {}
if (edge && json.number_of_seed_points === undefined) {
if (edge.edge_type == "Line" && edge.end_x === undefined) {
edge.end_x = parseFloat(value);
} else if (edge.edge_type == "Elliptic arc") {
edge.major_end_dx = parseFloat(value);
}
}
}
} else if (code == "12") {
if (json.subclass == "AcDbTrace" || json.subclass == "AcDbFace") {
if (!json.corners) json.corners = [];
json.corners.push({x: parseFloat(valu