@itwin/core-backend
Version:
iTwin.js backend components
139 lines • 7.37 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expect } from "chai";
import { CompressedId64Set, IModelStatus, OpenMode } from "@itwin/core-bentley";
import { LineSegment3d, Point3d, YawPitchRollAngles } from "@itwin/core-geometry";
import { Code, ColorByName, GeometryStreamBuilder, IModel, SubCategoryAppearance, } from "@itwin/core-common";
import { _nativeDb, ChannelControl, IModelJsFs, PhysicalModel, SpatialCategory, StandaloneDb, VolumeElement, } from "../../core-backend";
import { IModelTestUtils } from "../IModelTestUtils";
describe("Model geometry changes", () => {
let imodel;
let modelId;
let categoryId;
let lastChanges;
before(async () => {
const testFileName = IModelTestUtils.prepareOutputFile("ModelGeometryTracking", "ModelGeometryTracking.bim");
const seedFileName = IModelTestUtils.resolveAssetFile("test.bim");
IModelJsFs.copySync(seedFileName, testFileName);
// Upgrade the schema to include the GeometryGuid and LastMod model properties.
StandaloneDb.upgradeStandaloneSchemas(testFileName);
imodel = StandaloneDb.openFile(testFileName, OpenMode.ReadWrite);
imodel.channels.addAllowedChannel(ChannelControl.sharedChannelName);
modelId = PhysicalModel.insert(imodel, IModel.rootSubjectId, "TestModel");
categoryId = SpatialCategory.insert(imodel, IModel.dictionaryId, "TestCategory", new SubCategoryAppearance({ color: ColorByName.darkRed }));
imodel.saveChanges("set up");
imodel[_nativeDb].deleteAllTxns();
imodel.txns.onGeometryChanged.addListener((props) => lastChanges = props);
});
after(async () => {
imodel[_nativeDb].setGeometricModelTrackingEnabled(false);
imodel.close();
});
function expectChanges(expected) {
if (!expected) {
expect(lastChanges).to.be.undefined;
return;
}
expect(lastChanges).to.be.not.undefined;
expect(Array.isArray(lastChanges)).to.be.true;
expect(lastChanges.length).to.equal(1);
const actual = lastChanges[0];
expect(actual.id).to.equal(modelId);
const expectElements = (ids, exp) => {
expect(undefined === ids).to.equal(undefined === exp);
if (ids && exp) {
const act = CompressedId64Set.decompressArray(ids);
expect(act.length).to.equal(exp.length);
expect(act.sort()).to.deep.equal(exp.sort());
}
};
expectElements(actual.inserted?.ids, expected.inserted);
expectElements(actual.updated?.ids, expected.updated);
expectElements(actual.deleted, expected.deleted);
lastChanges = undefined;
}
function expectNoChanges() {
expect(lastChanges).to.be.undefined;
}
it("emits events", async () => {
expect(imodel[_nativeDb].isGeometricModelTrackingSupported()).to.be.true;
expect(imodel[_nativeDb].setGeometricModelTrackingEnabled(true).result).to.be.true;
const builder = new GeometryStreamBuilder();
builder.appendGeometry(LineSegment3d.create(Point3d.createZero(), Point3d.create(5, 0, 0)));
// Insert a geometric element.
const props = {
classFullName: VolumeElement.classFullName,
model: modelId,
category: categoryId,
code: Code.createEmpty(),
placement: {
origin: new Point3d(1, 2, 0),
angles: new YawPitchRollAngles(),
},
geom: builder.geometryStream,
};
const elemId0 = imodel.elements.insertElement(props);
imodel.saveChanges("insert elem 0");
expectChanges({ modelId, inserted: [elemId0] });
// Modify the element without touching its geometry.
props.userLabel = "new label";
props.id = elemId0;
imodel.elements.updateElement(props);
imodel.saveChanges("change label");
expectNoChanges();
// Modify the element's geometry.
props.placement = { origin: new Point3d(2, 1, 0), angles: new YawPitchRollAngles() };
imodel.elements.updateElement(props);
imodel.saveChanges("change placement");
expectChanges({ modelId, updated: [elemId0] });
// Insert another element.
props.id = undefined;
const elemId1 = imodel.elements.insertElement(props);
imodel.saveChanges("insert elem 1");
expectChanges({ modelId, inserted: [elemId1] });
// Delete an element.
imodel.elements.deleteElement(elemId0);
imodel.saveChanges("delete elem 0");
expectChanges({ modelId, deleted: [elemId0] });
// Stop tracking geometry changes
expect(imodel[_nativeDb].setGeometricModelTrackingEnabled(false).result).to.be.false;
expect(imodel[_nativeDb].isGeometricModelTrackingSupported()).to.be.true;
// Modify element's geometry.
props.id = elemId1;
props.placement = { origin: new Point3d(2, 10, 0), angles: new YawPitchRollAngles() };
imodel.elements.updateElement(props);
imodel.saveChanges("change placement again without tracking");
expectNoChanges();
// Restart tracking and undo everything.
expect(imodel[_nativeDb].setGeometricModelTrackingEnabled(true).result).to.be.true;
expect(imodel.txns.reverseSingleTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, updated: [elemId1] });
expect(imodel.txns.reverseSingleTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, inserted: [elemId0] });
expect(imodel.txns.reverseSingleTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, deleted: [elemId1] });
expect(imodel.txns.reverseSingleTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, updated: [elemId0] });
expect(imodel.txns.reverseSingleTxn()).to.equal(IModelStatus.Success);
expectNoChanges();
expect(imodel.txns.reverseSingleTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, deleted: [elemId0] });
// Redo everything.
expect(imodel.txns.reinstateTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, inserted: [elemId0] });
expect(imodel.txns.reinstateTxn()).to.equal(IModelStatus.Success);
expectNoChanges();
expect(imodel.txns.reinstateTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, updated: [elemId0] });
expect(imodel.txns.reinstateTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, inserted: [elemId1] });
expect(imodel.txns.reinstateTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, deleted: [elemId0] });
expect(imodel.txns.reinstateTxn()).to.equal(IModelStatus.Success);
expectChanges({ modelId, updated: [elemId1] });
expect(imodel[_nativeDb].setGeometricModelTrackingEnabled(false).result).to.be.false;
});
});
//# sourceMappingURL=GeometryChangeEvents.test.js.map