UNPKG

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
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