@mlightcad/data-model
Version:
The data-model package provides the core classes for interacting with AutoCAD's database and entities. This package mimics AutoCAD ObjectARX's AcDb (Database) classes and implements the drawing database structure that AutoCAD developers are familiar with.
448 lines • 22.7 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import { AcCmPerformanceCollector, AcCmTask, AcCmTaskScheduler } from '@mlightcad/common';
import { AcDbRenderingCache } from '../misc';
var PERFORMANCE_ENTRY_NAME = 'Load Database';
/**
* Task class for database conversion operations.
*
* This class extends AcCmTask to provide specialized functionality
* for database conversion tasks, including progress tracking and
* stage management.
*
* @template TIn - The input type for the task
* @template TOut - The output type for the task
*/
var AcDbConversionTask = /** @class */ (function (_super) {
__extends(AcDbConversionTask, _super);
function AcDbConversionTask(data, progress) {
var _this = _super.call(this, data.stage) || this;
_this.data = data;
_this.progress = progress;
return _this;
}
/**
* Executes the task.
*/
AcDbConversionTask.prototype.run = function (input) {
return __awaiter(this, void 0, void 0, function () {
var entry, t, out;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
entry = AcCmPerformanceCollector.getInstance().getEntry(PERFORMANCE_ENTRY_NAME);
t = Date.now();
if (!this.progress) return [3 /*break*/, 2];
return [4 /*yield*/, this.progress(this.data.progress.value, this.data.stage, 'START')];
case 1:
_a.sent();
_a.label = 2;
case 2: return [4 /*yield*/, this.data.task(input)];
case 3:
out = _a.sent();
if (!this.progress) return [3 /*break*/, 5];
return [4 /*yield*/, this.progress(this.data.progress.value, this.data.stage, 'END', out ? out.data : null)];
case 4:
_a.sent();
this.data.progress.value += this.data.step;
if (this.data.progress.value > 100) {
this.data.progress.value = 100;
}
_a.label = 5;
case 5:
if (entry) {
;
entry.data[this.name] = Date.now() - t;
}
return [2 /*return*/, out];
}
});
});
};
return AcDbConversionTask;
}(AcCmTask));
/**
* Abstract base class for database converters.
*
* This class provides the foundation for converting various file formats
* (such as DXF, DWG) into AcDbDatabase objects. It handles the conversion
* process in stages and provides progress tracking capabilities.
*
* @template TModel - The type of the parsed model data
*
* @example
* ```typescript
* class MyConverter extends AcDbDatabaseConverter<MyModel> {
* protected parse(data: string | ArrayBuffer): MyModel {
* // Implementation for parsing data
* }
*
* ......
*
* protected processEntities(model: MyModel, db: AcDbDatabase) {
* // Implementation for processing entities
* }
* }
* ```
*/
var AcDbDatabaseConverter = /** @class */ (function () {
/**
* Creates a new instance of the database converter.
*
* @param config - Configuration options for the converter. This includes settings
* such as worker URL for web workers used in the conversion process.
* If not provided, an empty configuration object will be used.
*
* @example
* ```typescript
* // Create converter with default configuration
* const converter = new AcDbDxfConverter();
*
* // Create converter with custom worker URL
* const converter = new AcDbDxfConverter({
* parserWorkerUrl: '/assets/dxf-parser-worker.js'
* });
* ```
*/
function AcDbDatabaseConverter(config) {
if (config === void 0) { config = {}; }
this.config = config;
}
/**
* Reads and converts data into an AcDbDatabase.
*
* This method orchestrates the entire conversion process, including
* parsing, processing various components (fonts, linetypes, styles, etc.),
* and building the final database.
*
* @param data - The input data to convert
* @param db - The database to populate with converted data
* @param minimumChunkSize - Minimum chunk size for batch processing
* @param progress - Optional progress callback
* @returns Promise that resolves when conversion is complete
*
*/
AcDbDatabaseConverter.prototype.read = function (data, db, minimumChunkSize, progress) {
return __awaiter(this, void 0, void 0, function () {
var loadDbTimeEntry, percentage, scheduler, t;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
loadDbTimeEntry = {
name: PERFORMANCE_ENTRY_NAME,
data: { total: 0 },
format: function () {
var _this = this;
var result = '';
Object.keys(this.data).forEach(function (key) {
if (key !== 'total') {
result += "- ".concat(key, ": ").concat(_this.data[key], " ms\n");
}
});
result += "- total: ".concat(this.data.total, " ms");
return result;
}
};
AcCmPerformanceCollector.getInstance().collect(loadDbTimeEntry);
this.progress = progress;
percentage = { value: 0 };
scheduler = new AcCmTaskScheduler();
scheduler.setCompleteCallback(function () { return _this.onFinished(); });
scheduler.setErrorCallback(function (error) { return _this.onError(error); });
scheduler.addTask(new AcDbConversionTask({
stage: 'START',
step: 1,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'PARSE',
step: 5,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.parse(data)];
case 1: return [2 /*return*/, _a.sent()];
}
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'FONT',
step: 5,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
var fonts;
return __generator(this, function (_a) {
fonts = this.getFonts(data.model);
return [2 /*return*/, { model: data.model, data: fonts }];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'LTYPE',
step: 1,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.processLineTypes(data.model, db);
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'STYLE',
step: 1,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.processTextStyles(data.model, db);
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'DIMSTYLE',
step: 1,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.processDimStyles(data.model, db);
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'LAYER',
step: 1,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.processLayers(data.model, db);
// Guarantee layer '0' is created at least
if (db.tables.layerTable.numEntries === 0) {
db.createDefaultData({ layer: true });
}
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'VPORT',
step: 1,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.processViewports(data.model, db);
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'HEADER',
step: 1,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.processHeader(data.model, db);
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'BLOCK_RECORD',
step: 5,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.processBlockTables(data.model, db);
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'OBJECT',
step: 5,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.processObjects(data.model, db);
// Guarantee one layout is created for MODEL_SPACE at least
if (db.dictionaries.layouts.numEntries === 0) {
db.createDefaultData({ layout: true });
}
return [2 /*return*/, data];
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'BLOCK',
step: 5,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.processBlocks(data.model, db)];
case 1:
_a.sent();
return [2 /*return*/, data];
}
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'ENTITY',
step: 100,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.processEntities(data.model, db, minimumChunkSize, percentage, progress)];
case 1:
_a.sent();
return [2 /*return*/, data];
}
});
}); }
}, progress));
scheduler.addTask(new AcDbConversionTask({
stage: 'END',
step: 0,
progress: percentage,
task: function (data) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, data];
});
}); }
}, progress));
t = Date.now();
return [4 /*yield*/, scheduler.run(data)];
case 1:
_a.sent();
loadDbTimeEntry.data.total = Date.now() - t;
return [2 /*return*/];
}
});
});
};
AcDbDatabaseConverter.prototype.onError = function (error) {
if (this.progress) {
var task = error.task;
this.progress(task.data.progress.value, task.data.stage, 'ERROR', undefined, error);
}
console.error("Error occurred in conversion stage '".concat(error.task.name, "': "), error.error);
// Tasks to convert entities are not critical to the conversion process
// If failed to convert certain entities, we can still continue to convert
// the rest of entities.
if (error.task.name === 'ENTITY') {
return false;
}
this.onFinished();
return true;
};
AcDbDatabaseConverter.prototype.onFinished = function () {
if (this.progress) {
this.progress(100, 'END', 'END');
// Clear cache to reduce memory consumption
AcDbRenderingCache.instance.clear();
}
};
AcDbDatabaseConverter.prototype.parse = function (_data, _workerUrl) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
throw new Error('Not impelemented yet!');
});
});
};
AcDbDatabaseConverter.prototype.getFonts = function (_model) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processLineTypes = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processTextStyles = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processDimStyles = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processLayers = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processViewports = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processHeader = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processBlockTables = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processObjects = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processBlocks = function (_model, _db) {
throw new Error('Not impelemented yet!');
};
AcDbDatabaseConverter.prototype.processEntities = function (_model, _db, _minimumChunkSize, _percentage, _progress) {
throw new Error('Not impelemented yet!');
};
return AcDbDatabaseConverter;
}());
export { AcDbDatabaseConverter };
//# sourceMappingURL=AcDbDatabaseConverter.js.map