@itwin/core-backend
Version:
iTwin.js backend components
842 lines • 176 kB
JavaScript
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner;
if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
if (async) inner = dispose;
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
return function (env) {
function fail(e) {
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true;
}
var r, s = 0;
function next() {
while (r = env.stack.pop()) {
try {
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
if (r.dispose) {
var result = r.dispose.call(r.value);
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
}
else s |= 1;
}
catch (e) {
fail(e);
}
}
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
if (env.hasError) throw env.error;
}
return next();
};
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { assert, expect } from "chai";
import * as path from "path";
import * as semver from "semver";
import * as sinon from "sinon";
import { DbResult, Guid, Id64, IModelStatus, Logger, OpenMode, ProcessDetector } from "@itwin/core-bentley";
import { BisCodeSpec, BriefcaseIdValue, Code, CodeScopeSpec, CodeSpec, ColorByName, ColorDef, DisplayStyleSettings, EcefLocation, FontMap, FontType, GeoCoordStatus, GeographicCRS, GeometryParams, GeometryStreamBuilder, ImageSourceFormat, IModel, IModelError, RelatedElement, RenderMode, SchemaState, SubCategoryAppearance, TextureMapping, TextureMapUnits, ViewFlags, } from "@itwin/core-common";
import { Geometry, LineString3d, Loop, Matrix4d, Point3d, PolyfaceBuilder, Range3d, StrokeOptions, Transform, YawPitchRollAngles, } from "@itwin/core-geometry";
import { V2CheckpointManager } from "../../CheckpointManager";
import { _nativeDb, BisCoreSchema, Category, ClassRegistry, DefinitionContainer, DefinitionGroup, DefinitionGroupGroupsDefinitions, DefinitionModel, DefinitionPartition, DictionaryModel, DisplayStyle3d, DocumentPartition, DrawingGraphic, Element, ElementDrivesElement, ElementGroupsMembers, ElementOwnsChildElements, GenericGraphicalType2d, GeometricElement2d, GeometricElement3d, GeometricModel, GroupInformationPartition, IModelDb, IModelHost, IModelJsFs, InformationPartitionElement, InformationRecordElement, LinkPartition, Model, PhysicalElement, PhysicalModel, PhysicalObject, PhysicalPartition, RenderMaterialElement, SnapshotDb, SpatialCategory, SqliteValueType, StandaloneDb, SubCategory, Subject, Texture, } from "../../core-backend";
import { BriefcaseDb } from "../../IModelDb";
import { HubMock } from "../../internal/HubMock";
import { KnownTestLocations } from "../KnownTestLocations";
import { IModelTestUtils } from "../IModelTestUtils";
import { DisableNativeAssertions } from "../TestUtils";
import { samplePngTexture } from "../imageData";
import { performance } from "perf_hooks";
import { _hubAccess } from "../../internal/Symbols";
import { CustomAttributeClass, EntityClass, PropertyType, propertyTypeToString, SchemaItemType } from "@itwin/ecschema-metadata";
// spell-checker: disable
async function getIModelError(promise) {
try {
await promise;
return undefined;
}
catch (err) {
return err instanceof IModelError ? err : undefined;
}
}
function expectIModelError(expectedErrorNumber, error) {
expect(error).not.to.be.undefined;
expect(error).instanceof(IModelError);
expect(error.errorNumber).to.equal(expectedErrorNumber);
}
async function generateTestSnapshot(targetFileName, seedAssetName) {
const seedFile = IModelTestUtils.resolveAssetFile(seedAssetName);
const snapshotFile = IModelTestUtils.prepareOutputFile("IModel", targetFileName);
const imodel = IModelTestUtils.createSnapshotFromSeed(snapshotFile, seedFile);
const schemaPathname = path.join(KnownTestLocations.assetsDir, "TestBim.ecschema.xml");
await imodel.importSchemas([schemaPathname]); // will throw an exception if import fails
imodel.saveChanges();
return imodel;
}
describe("iModel", () => {
//TODO: These imodels are used and modified across multiple tests. This is not a good practice and should be refactored.
let imodel1;
let imodel2;
let imodel3;
let imodel4;
let imodel5;
let originalEnv;
before(async () => {
originalEnv = { ...process.env };
IModelTestUtils.registerTestBimSchema();
imodel1 = await generateTestSnapshot("test.bim", "test.bim");
imodel2 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "CompatibilityTestSeed.bim"), IModelTestUtils.resolveAssetFile("CompatibilityTestSeed.bim"));
imodel3 = SnapshotDb.openFile(IModelTestUtils.resolveAssetFile("GetSetAutoHandledStructProperties.bim"));
imodel4 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "GetSetAutoHandledArrayProperties.bim"), IModelTestUtils.resolveAssetFile("GetSetAutoHandledArrayProperties.bim"));
imodel5 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "mirukuru.ibim"), IModelTestUtils.resolveAssetFile("mirukuru.ibim"));
});
after(() => {
process.env = originalEnv;
imodel1.close();
imodel2.close();
imodel3.close();
imodel4.close();
imodel5.close();
});
afterEach(() => {
sinon.restore();
});
/** Roundtrip the entity through a json string and back to a new entity. */
const roundtripThroughJson = (entity1) => {
const string1 = JSON.stringify(entity1);
const props1 = JSON.parse(string1);
const entity2 = new entity1.constructor(props1, entity1.iModel); // create a new entity from the EntityProps
const string2 = JSON.stringify(entity2);
assert.equal(string1, string2);
return entity2;
};
it("should be able to get properties of an iIModel", () => {
expect(imodel1.name).equals("TBD"); // That's the name of the root subject!
const extents = imodel1.projectExtents;
assert(!extents.isNull);
// make sure we can construct a new element even if we haven't loaded its metadata (will be loaded in ctor)
// eslint-disable-next-line @typescript-eslint/no-deprecated
assert.isUndefined(imodel1.classMetaDataRegistry.find("biscore:lightlocation"));
const e1 = imodel1.constructEntity({ category: "0x11", classFullName: "BisCore:LightLocation", model: "0x01", code: Code.createEmpty() });
assert.isDefined(e1);
// eslint-disable-next-line @typescript-eslint/no-deprecated
assert.isDefined(imodel1.classMetaDataRegistry.find("biscore:lightlocation")); // should have been loaded in ctor
});
it("should use schema to look up classes by name", () => {
const elementClass = ClassRegistry.findRegisteredClass(Element.classFullName);
const categoryClass = ClassRegistry.findRegisteredClass(Category.classFullName);
assert.isDefined(elementClass);
assert.isDefined(categoryClass);
assert.equal(elementClass.schema, BisCoreSchema);
assert.equal(categoryClass.schema, BisCoreSchema);
assert.equal(elementClass.className, "Element");
assert.equal(categoryClass.className, "Category");
});
it("Fonts", () => {
const dbFonts = imodel1.fonts;
expect(Array.from(dbFonts.queryMappedFamilies({ includeNonEmbedded: true })).length).to.equal(4);
expect(dbFonts.findDescriptor(1)).to.deep.equal({ name: "Arial", type: FontType.TrueType });
expect(dbFonts.findId({ name: "Arial" })).to.equal(1);
expect(dbFonts.findId({ name: "arial" })).to.equal(1);
expect(dbFonts.findDescriptor(2)).to.deep.equal({ name: "Font0", type: FontType.Rsc });
expect(dbFonts.findId({ name: "Font0" })).to.equal(2);
expect(dbFonts.findId({ name: "fOnt0" })).to.equal(2);
expect(dbFonts.findDescriptor(3)).to.deep.equal({ name: "ShxFont0", type: FontType.Shx });
expect(dbFonts.findId({ name: "ShxFont0" })).to.equal(3);
expect(dbFonts.findId({ name: "shxfont0" })).to.equal(3);
expect(dbFonts.findDescriptor(4)).to.deep.equal({ name: "Calibri", type: FontType.TrueType });
expect(dbFonts.findId({ name: "Calibri" })).to.equal(4);
expect(dbFonts.findId({ name: "cAlIbRi" })).to.equal(4);
expect(dbFonts.findId({ name: "notfound" })).to.be.undefined;
const fonts1 = imodel1.fontMap; // eslint-disable-line @typescript-eslint/no-deprecated
assert.equal(fonts1.fonts.size, 4, "font map size should be 4");
assert.equal(FontType.TrueType, fonts1.getFont(1).type, "get font 1 type is TrueType");
assert.equal("Arial", fonts1.getFont(1).name, "get Font 1 name");
assert.equal(1, fonts1.getFont("Arial").id, "get Font 1, by name");
assert.equal(1, fonts1.getFont("arial").id, "get Font 1, by name case insensitive");
assert.equal(FontType.Rsc, fonts1.getFont(2).type, "get font 2 type is Rsc");
assert.equal("Font0", fonts1.getFont(2).name, "get Font 2 name");
assert.equal(2, fonts1.getFont("Font0").id, "get Font 2, by name");
assert.equal(2, fonts1.getFont("fOnt0").id, "get Font 2, by name case insensitive");
assert.equal(FontType.Shx, fonts1.getFont(3).type, "get font 1 type is Shx");
assert.equal("ShxFont0", fonts1.getFont(3).name, "get Font 3 name");
assert.equal(3, fonts1.getFont("ShxFont0").id, "get Font 3, by name");
assert.equal(3, fonts1.getFont("shxfont0").id, "get Font 3, by name case insensitive");
assert.equal(FontType.TrueType, fonts1.getFont(4).type, "get font 4 type is TrueType");
assert.equal("Calibri", fonts1.getFont(4).name, "get Font 4 name");
assert.equal(4, fonts1.getFont("Calibri").id, "get Font 4, by name");
assert.equal(4, fonts1.getFont("cAlIbRi").id, "get Font 4, by name case insensitive");
assert.isUndefined(fonts1.getFont("notfound"), "attempt lookup of a font that should not be found");
assert.deepEqual(new FontMap(fonts1.toJSON()), fonts1, "toJSON on FontMap"); // eslint-disable-line @typescript-eslint/no-deprecated
});
it("should load a known element by Id from an existing iModel", () => {
assert.exists(imodel1.elements);
const code1 = new Code({ spec: "0x10", scope: "0x11", value: "RF1.dgn" });
const el = imodel1.elements.getElement(code1);
assert.exists(el);
const el2ById = imodel1.elements.getElement("0x34");
assert.exists(el2ById);
const badCode = new Code({ spec: "0x10", scope: "0x11", value: "RF1_does_not_exist.dgn" });
try {
imodel1.elements.getElement(badCode); // throws Error
assert.fail(); // this line should be skipped
}
catch (error) {
assert.instanceOf(error, Error);
assert.instanceOf(error, IModelError);
assert.equal(error.errorNumber, IModelStatus.NotFound);
}
const element1 = imodel1.elements.tryGetElement(code1);
const element2 = imodel1.elements.tryGetElement("0x34");
const element3 = imodel1.elements.tryGetElement(badCode);
assert.isDefined(element1);
assert.isDefined(element2);
assert.isUndefined(element3);
const elementProps1 = imodel1.elements.tryGetElementProps(code1);
const elementProps2 = imodel1.elements.tryGetElementProps("0x34");
const elementProps3 = imodel1.elements.tryGetElementProps(badCode);
assert.isDefined(elementProps1);
assert.isDefined(elementProps2);
assert.isUndefined(elementProps3);
const model1 = imodel1.models.tryGetModel(IModel.dictionaryId);
const modelProps1 = imodel1.models.tryGetModelProps(IModel.dictionaryId);
const subModel1 = imodel1.models.tryGetSubModel(IModel.dictionaryId);
assert.isDefined(model1);
assert.isDefined(modelProps1);
assert.isDefined(subModel1);
const badModel1 = imodel1.models.tryGetModel(Id64.fromUint32Pair(999, 999));
const badModelProps1 = imodel1.models.tryGetModelProps(Id64.fromUint32Pair(999, 999));
const badSubModel1 = imodel1.models.tryGetSubModel(IModel.rootSubjectId);
const badSubModel2 = imodel1.models.tryGetSubModel(badCode);
assert.isUndefined(badModel1);
assert.isUndefined(badModelProps1);
assert.isUndefined(badSubModel1);
assert.isUndefined(badSubModel2);
const subCat = imodel1.elements.getElement("0x2e");
assert.isTrue(subCat instanceof SubCategory);
if (subCat instanceof SubCategory) {
assert.isTrue(subCat.appearance.color.tbgr === 16777215);
assert.isTrue(subCat.appearance.weight === 2);
assert.equal(Id64.getLocalId(subCat.id), 46);
assert.equal(Id64.getBriefcaseId(subCat.id), 0);
assert.equal(Id64.getLocalId(subCat.code.spec), 30);
assert.equal(Id64.getBriefcaseId(subCat.code.spec), 0);
assert.isTrue(subCat.code.scope === "0x2d");
assert.isTrue(subCat.code.value === "A-Z013-G-Legn");
roundtripThroughJson(subCat);
}
/// Get the parent Category of the subcategory.
const cat = imodel1.elements.getElement(subCat.getCategoryId());
assert.isTrue(cat instanceof Category);
if (cat instanceof Category) {
assert.equal(Id64.getLocalId(cat.id), 45);
assert.equal(Id64.getBriefcaseId(cat.id), 0);
assert.isTrue(cat.description === "Legends, symbols keys");
assert.equal(Id64.getLocalId(cat.code.spec), 22);
assert.equal(Id64.getBriefcaseId(cat.code.spec), 0);
assert.isTrue(cat.code.value === "A-Z013-G-Legn");
roundtripThroughJson(cat);
}
const phys = imodel1.elements.getElement("0x38");
assert.isTrue(phys instanceof GeometricElement3d);
const locateMsg = phys.getToolTipMessage();
assert.isDefined(locateMsg);
const a2 = imodel2.elements.getElement("0x1d");
assert.exists(a2);
expect(a2.federationGuid).equal("18eb4650-b074-414f-b961-d9cfaa6c8746");
const el3 = imodel2.elements.getElement(a2.federationGuid);
assert.exists(el3);
assert.notEqual(a2, el3);
assert.equal(a2.id, el3.id);
roundtripThroughJson(el3);
const newEl = el3.toJSON();
newEl.federationGuid = undefined;
newEl.code = { scope: "bad scope", spec: "0x10", value: "new code" };
expect(() => imodel2.elements.insertElement(newEl)).throws("invalid code scope").to.have.property("metadata");
newEl.code.scope = "0x34322"; // valid id, but element doesn't exist
expect(() => imodel2.elements.insertElement(newEl)).throws("invalid code scope").to.have.property("metadata");
newEl.code.scope = el3.federationGuid;
const newId = imodel2.elements.insertElement(newEl); // code scope from FederationGuid should get converted to ElementId
const a4 = imodel2.elements.getElementProps(newId);
expect(a4.code.scope).equal(el3.id);
a4.code.scope = "0x13343";
expect(() => imodel2.elements.updateElement(a4)).throws("invalid code scope").to.have.property("metadata");
a4.code.scope = "0x1";
imodel2.elements.updateElement(a4); // should change the code scope to new element
let a5 = imodel2.elements.getElementProps(newId);
expect(a5.code.scope).equal("0x1");
// only pass minimum, but expect model and classFullName to be added.
const newProps = { id: a4.id, code: a4.code, classFullName: undefined, model: undefined };
newProps.code.scope = el3.federationGuid; // should convert FederationGuid to ElementId
imodel2.elements.updateElement(newProps);
expect(newProps.classFullName).eq(a4.classFullName);
expect(newProps.model).eq(a4.model);
a5 = imodel2.elements.getElementProps(newId);
expect(a5.code.scope).equal(el3.id);
});
it("should optionally detect class mismatches", () => {
// tryGetElement
const subjectUnvalidated = imodel1.elements.tryGetElement(IModel.rootSubjectId);
assert.isDefined(subjectUnvalidated);
const subjectValidated = imodel1.elements.tryGetElement(IModel.rootSubjectId, Subject);
assert.isDefined(subjectValidated);
const physicalElementUnvalidated = imodel1.elements.tryGetElement(IModel.rootSubjectId);
assert.isDefined(physicalElementUnvalidated); // wrong type, but class to validate was not passed
const physicalElementValidated = imodel1.elements.tryGetElement(IModel.rootSubjectId, PhysicalElement); // abstract class
assert.isUndefined(physicalElementValidated); // wrong type
const physicalObjectUnvalidated = imodel1.elements.tryGetElement(IModel.rootSubjectId);
assert.isDefined(physicalObjectUnvalidated); // wrong type, but class to validate was not passed
const physicalObjectValidated = imodel1.elements.tryGetElement(IModel.rootSubjectId, PhysicalObject); // concrete class
assert.isUndefined(physicalObjectValidated); // wrong type
// tryGetModel
const dictionaryUnvalidated = imodel1.models.tryGetModel(IModel.dictionaryId);
assert.isDefined(dictionaryUnvalidated);
const dictionaryValidated = imodel1.models.tryGetModel(IModel.dictionaryId, DictionaryModel);
assert.isDefined(dictionaryValidated);
const geometricModelUnvalidated = imodel1.models.tryGetModel(IModel.dictionaryId);
assert.isDefined(geometricModelUnvalidated); // wrong type, but class to validate was not passed
const geometricModelValidated = imodel1.models.tryGetModel(IModel.dictionaryId, GeometricModel); // abstract class
assert.isUndefined(geometricModelValidated); // wrong type
const physicalModelUnvalidated = imodel1.models.tryGetModel(IModel.dictionaryId);
assert.isDefined(physicalModelUnvalidated); // wrong type, but class to validate was not passed
const physicalModelValidated = imodel1.models.tryGetModel(IModel.dictionaryId, PhysicalModel); // concrete class
assert.isUndefined(physicalModelValidated); // wrong type
// tryGetSubModel
const dictionarySubUnvalidated = imodel1.models.tryGetSubModel(IModel.dictionaryId);
assert.isDefined(dictionarySubUnvalidated);
const dictionarySubValidated = imodel1.models.tryGetSubModel(IModel.dictionaryId, DictionaryModel);
assert.isDefined(dictionarySubValidated);
const geometricSubModelUnvalidated = imodel1.models.tryGetSubModel(IModel.dictionaryId);
assert.isDefined(geometricSubModelUnvalidated); // wrong type, but class to validate was not passed
const geometricSubModelValidated = imodel1.models.tryGetSubModel(IModel.dictionaryId, GeometricModel); // abstract class
assert.isUndefined(geometricSubModelValidated); // wrong type
const physicalSubModelUnvalidated = imodel1.models.tryGetSubModel(IModel.dictionaryId);
assert.isDefined(physicalSubModelUnvalidated); // wrong type, but class to validate was not passed
const physicalSubModelValidated = imodel1.models.tryGetSubModel(IModel.dictionaryId, PhysicalModel); // concrete class
assert.isUndefined(physicalSubModelValidated); // wrong type
});
it("should create elements", () => {
const seedElement = imodel2.elements.getElement("0x1d");
assert.exists(seedElement);
assert.isTrue(seedElement.federationGuid === "18eb4650-b074-414f-b961-d9cfaa6c8746");
for (let i = 0; i < 25; i++) {
const elementProps = {
classFullName: "Generic:PhysicalObject",
model: seedElement.model,
category: seedElement.category,
code: Code.createEmpty(),
federationGuid: Guid.createValue(),
userLabel: `UserLabel-${i}`,
};
const element = imodel2.elements.createElement(elementProps);
element.setUserProperties("performanceTest", { s: `String-${i}`, n: i });
const elementId = imodel2.elements.insertElement(element.toJSON());
assert.isTrue(Id64.isValidId64(elementId));
}
});
it("should insert a RenderMaterial", () => {
const model = imodel2.models.getModel(IModel.dictionaryId);
expect(model).not.to.be.undefined;
const testMaterialName = "test material name";
const testPaletteName = "test palette name";
const testDescription = "test description";
const color = [0.3, 0.7, 0.8];
const specularColor = [0.1, 1, 0];
const finish = 0.4;
const transmit = 0.1;
const diffuse = 0.24;
const specular = 0.9;
const reflect = 0.3;
const reflectColor = [1, 0, 0.5];
/* eslint-disable @typescript-eslint/naming-convention */
const textureMapProps = {
pattern_angle: 3.0,
pattern_u_flip: false,
pattern_flip: false,
pattern_scale: [1.0, 1.0],
pattern_offset: [0.0, 0.0],
pattern_scalemode: TextureMapUnits.Inches,
pattern_mapping: TextureMapping.Mode.Planar,
pattern_weight: 0.5,
TextureId: "test_textureid",
};
/* eslint-enable @typescript-eslint/naming-convention */
const renderMaterialParams = {
paletteName: testPaletteName,
description: testDescription,
color,
specularColor,
finish,
transmit,
diffuse,
specular,
reflect,
reflectColor,
patternMap: textureMapProps,
};
const renderMaterialId = RenderMaterialElement.insert(imodel2, IModel.dictionaryId, testMaterialName, renderMaterialParams);
const renderMaterial = imodel2.elements.getElement(renderMaterialId);
assert((renderMaterial instanceof RenderMaterialElement) === true, "did not retrieve an instance of RenderMaterial");
expect(renderMaterial.paletteName).to.equal(testPaletteName);
expect(renderMaterial.description).to.equal(testDescription);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.HasBaseColor).to.equal(true);
expect(JSON.stringify(renderMaterial.jsonProperties.materialAssets.renderMaterial.color)).to.equal(JSON.stringify(color));
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.HasSpecularColor).to.equal(true);
expect(JSON.stringify(renderMaterial.jsonProperties.materialAssets.renderMaterial.specular_color)).to.equal(JSON.stringify(specularColor));
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.HasFinish).to.equal(true);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.finish).to.equal(finish);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.HasTransmit).to.equal(true);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.transmit).to.equal(transmit);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.HasDiffuse).to.equal(true);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.diffuse).to.equal(diffuse);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.HasSpecular).to.equal(true);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.specular).to.equal(specular);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.HasReflect).to.equal(true);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.reflect).to.equal(reflect);
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.HasReflectColor).to.equal(true);
expect(JSON.stringify(renderMaterial.jsonProperties.materialAssets.renderMaterial.reflect_color)).to.equal(JSON.stringify(reflectColor));
expect(renderMaterial.jsonProperties.materialAssets.renderMaterial.Map).not.to.be.undefined;
const patternMap = renderMaterial.jsonProperties.materialAssets.renderMaterial.Map.Pattern;
expect(patternMap).not.to.be.undefined;
expect(patternMap.pattern_angle).to.equal(textureMapProps.pattern_angle);
expect(patternMap.pattern_u_flip).to.equal(textureMapProps.pattern_u_flip);
expect(patternMap.pattern_flip).to.equal(textureMapProps.pattern_flip);
expect(JSON.stringify(patternMap.pattern_scale)).to.equal(JSON.stringify(textureMapProps.pattern_scale));
expect(JSON.stringify(patternMap.pattern_offset)).to.equal(JSON.stringify(textureMapProps.pattern_offset));
expect(patternMap.pattern_scalemode).to.equal(textureMapProps.pattern_scalemode);
expect(patternMap.pattern_mapping).to.equal(textureMapProps.pattern_mapping);
expect(patternMap.pattern_weight).to.equal(textureMapProps.pattern_weight);
expect(patternMap.TextureId).to.equal(textureMapProps.TextureId);
});
it("attempt to apply material to new element in imodel5", () => {
const testTextureName = "fake texture name";
const testTextureFormat = ImageSourceFormat.Png;
const testTextureDescription = "empty description";
const texId = Texture.insertTexture(imodel5, IModel.dictionaryId, testTextureName, testTextureFormat, samplePngTexture.base64, testTextureDescription);
/* eslint-disable @typescript-eslint/naming-convention */
const matId = RenderMaterialElement.insert(imodel5, IModel.dictionaryId, "test material name", {
paletteName: "TestPaletteName",
patternMap: {
TextureId: texId,
pattern_offset: [0, 0],
pattern_scale: [1, 1],
pattern_scalemode: TextureMapUnits.Relative,
},
});
/* eslint-enable @typescript-eslint/naming-convention */
/** Create a simple flat mesh with 4 points (2x2) */
const width = imodel5.projectExtents.xLength() * 0.2;
const height = imodel5.projectExtents.yLength() * 0.2;
let shape;
const doPolyface = true;
if (doPolyface) {
const options = StrokeOptions.createForFacets();
options.shouldTriangulate = false;
const builder = PolyfaceBuilder.create(options);
const quad = [
Point3d.create(0.0, 0.0, 0.0),
Point3d.create(width, 0.0, 0.0),
Point3d.create(width, height, 0.0),
Point3d.create(0.0, height, 0.0),
];
builder.addQuadFacet(quad);
shape = builder.claimPolyface();
}
else {
shape = Loop.create(LineString3d.create([
Point3d.create(0, 0, 0),
Point3d.create(width, 0, 0),
Point3d.create(width, height, 0),
Point3d.create(0, height, 0),
Point3d.create(0, 0, 0),
]));
}
const modelId = PhysicalModel.insert(imodel5, IModelDb.rootSubjectId, "test_render_material_model_name");
const categoryId = SpatialCategory.insert(imodel5, IModel.dictionaryId, "GeoJSON Feature", { color: ColorDef.white.toJSON() });
/** generate a geometry stream containing the polyface */
const gsBuilder = new GeometryStreamBuilder();
const params = new GeometryParams(categoryId);
params.materialId = matId;
gsBuilder.appendGeometryParamsChange(params);
gsBuilder.appendGeometry(shape);
const geometry = gsBuilder.geometryStream;
// geometry[0].material = { materialId: matId };
const props = {
classFullName: "Generic:PhysicalObject",
placement: { origin: imodel5.projectExtents.center, angles: new YawPitchRollAngles() },
model: modelId,
code: Code.createEmpty(),
category: categoryId,
geom: geometry,
};
imodel5.elements.insertElement(props);
imodel5.saveChanges();
});
it("should insert a DisplayStyle", () => {
const model = imodel2.models.getModel(IModel.dictionaryId);
expect(model).not.to.be.undefined;
const settings = {
backgroundColor: ColorDef.blue.toJSON(),
viewflags: ViewFlags.fromJSON({
renderMode: RenderMode.SolidFill,
}),
};
const props = {
classFullName: DisplayStyle3d.classFullName,
model: IModel.dictionaryId,
code: { spec: BisCodeSpec.displayStyle, scope: IModel.dictionaryId, value: "test style" },
isPrivate: false,
jsonProperties: {
styles: settings,
},
};
const styleId = imodel2.elements.insertElement(props);
let style = imodel2.elements.getElement(styleId);
expect(style instanceof DisplayStyle3d).to.be.true;
expect(style.code.spec).equal(imodel2.codeSpecs.getByName(BisCodeSpec.displayStyle).id);
expect(style.settings.viewFlags.renderMode).to.equal(RenderMode.SolidFill);
expect(style.settings.backgroundColor.equals(ColorDef.blue)).to.be.true;
const newFlags = style.settings.viewFlags.copy({ renderMode: RenderMode.SmoothShade });
style.settings.viewFlags = newFlags;
style.settings.backgroundColor = ColorDef.red;
style.settings.monochromeColor = ColorDef.green;
expect(style.jsonProperties.styles.viewflags.renderMode).to.equal(RenderMode.SmoothShade);
imodel2.elements.updateElement(style.toJSON());
style = imodel2.elements.getElement(styleId);
expect(style instanceof DisplayStyle3d).to.be.true;
expect(style.settings.viewFlags.renderMode).to.equal(RenderMode.SmoothShade);
expect(style.settings.backgroundColor.equals(ColorDef.red)).to.be.true;
expect(style.settings.monochromeColor.equals(ColorDef.green)).to.be.true;
});
it("should create display styles", () => {
const defaultViewFlags = new ViewFlags().toJSON();
const defaultMapImagery = new DisplayStyleSettings({}).toJSON().mapImagery;
const viewFlags = new ViewFlags({ patterns: false, visibleEdges: true });
const viewflags = { noWhiteOnWhiteReversal: true, shadows: true, noTransp: true };
const mapImagery = {
backgroundBase: ColorDef.red.tbgr,
backgroundLayers: [{
name: "x",
url: "y",
transparency: 0.5,
formatId: "WMS",
visible: true,
}],
};
const props = {
mapImagery,
excludedElements: ["0x123", "0xfed"],
timePoint: 42,
backgroundColor: ColorDef.green.tbgr,
};
const testCases = [
[undefined, defaultViewFlags, false],
[{ viewFlags }, viewFlags.toJSON(), false],
[{ viewflags }, viewflags, false],
[{ viewflags, viewFlags }, viewFlags.toJSON(), false],
[props, defaultViewFlags, false],
[{ ...props, viewflags }, viewflags, false],
[{ backgroundColor: ColorDef.blue }, defaultViewFlags, false],
[{ backgroundColor: ColorDef.from(1, 2, 3, 4) }, defaultViewFlags, false],
[{ backgroundColor: ColorDef.blue.tbgr }, defaultViewFlags, false],
[{ backgroundColor: ColorDef.from(1, 2, 3, 4).tbgr }, defaultViewFlags, false],
];
let suffix = 123;
for (const test of testCases) {
const expected = test[0] ?? {};
const styleId = DisplayStyle3d.insert(imodel2, IModel.dictionaryId, `TestStyle${suffix++}`, expected);
const style = imodel2.elements.getElement(styleId).toJSON();
expect(style.jsonProperties.styles).not.to.be.undefined;
expect(style.jsonProperties).not.to.be.undefined;
expect(style.jsonProperties.styles).not.to.be.undefined;
const actual = style.jsonProperties.styles;
expect(actual.viewflags).not.to.be.undefined;
const expectedVf = ViewFlags.fromJSON(test[1]);
const actualVf = ViewFlags.fromJSON(actual.viewflags);
expect(actualVf.toJSON()).to.deep.equal(expectedVf.toJSON());
const expectedBGColor = expected.backgroundColor instanceof ColorDef ? expected.backgroundColor.toJSON() : expected.backgroundColor;
expect(actual.backgroundColor).to.equal(expectedBGColor);
// DisplayStyleSettings constructor always initializes json.mapImagery.
expect(actual.mapImagery).to.deep.equal(expected.mapImagery ?? defaultMapImagery);
expect(actual.excludedElements).to.deep.equal(expected.excludedElements);
expect(actual.timePoint).to.deep.equal(expected.timePoint);
}
});
it("should have a valid root subject element", () => {
const rootSubject = imodel1.elements.getRootSubject();
assert.exists(rootSubject);
assert.isTrue(rootSubject instanceof Subject);
assert.isAtLeast(rootSubject.code.value.length, 1);
assert.isFalse(imodel1.elements.hasSubModel(IModel.rootSubjectId));
try {
imodel1.models.getSubModel(rootSubject.id); // throws error
assert.fail(); // this line should be skipped
}
catch (error) {
assert.isTrue(error instanceof Error);
assert.isTrue(error instanceof IModelError);
assert.equal(error.errorNumber, IModelStatus.NotFound);
}
const childIds = imodel1.elements.queryChildren(rootSubject.id);
assert.isAtLeast(childIds.length, 1);
for (const childId of childIds) {
const childElement = imodel1.elements.getElement(childId);
assert.exists(childElement);
assert.isTrue(childElement instanceof Element);
roundtripThroughJson(childElement);
assert.equal(rootSubject.id, childElement.parent.id);
const childLocalId = Id64.getLocalId(childId);
const childBcId = Id64.getBriefcaseId(childId);
if (childElement instanceof InformationPartitionElement) {
assert.isTrue(imodel1.elements.hasSubModel(childElement.id));
const childSubModel = imodel1.models.getSubModel(childElement.id);
assert.exists(childSubModel, "InformationPartitionElements should have a subModel");
if (childLocalId === 16 && childBcId === 0) {
assert.isTrue(childElement instanceof DefinitionPartition, "ChildId 0x00000010 should be a DefinitionPartition");
assert.isTrue(childElement.code.value === "BisCore.DictionaryModel", "Definition Partition should have code value of BisCore.DictionaryModel");
}
else if (childLocalId === 14 && childBcId === 0) {
assert.isTrue(childElement instanceof LinkPartition);
assert.isTrue(childElement.code.value === "BisCore.RealityDataSources");
}
else if (childLocalId === 17 && childBcId === 0) {
assert.isTrue(childElement instanceof LinkPartition, "ChildId 0x000000011 should be a LinkPartition");
assert.isTrue(childElement.code.value === "Repository Links");
}
}
else if (childElement instanceof Subject) {
assert.isFalse(imodel1.elements.hasSubModel(childElement.id));
if (childLocalId === 19 && childBcId === 0) {
assert.isTrue(childElement instanceof Subject);
assert.isTrue(childElement.code.value === "DgnV8:mf3, A", "Subject should have code value of DgnV8:mf3, A");
assert.isTrue(childElement.jsonProperties.Subject.Job.DgnV8.V8File === "mf3.dgn", "Subject should have jsonProperty Subject.Job.DgnV.V8File");
assert.isTrue(childElement.jsonProperties.Subject.Job.DgnV8.V8RootModel === "A", "Subject should have jsonProperty Subject.Job.DgnV.V8RootModel");
}
}
}
});
it("should load a known model by Id from an existing iModel", () => {
assert.exists(imodel1.models);
const model2 = imodel1.models.getModel("0x1c");
assert.exists(model2);
const formatter = model2.getJsonProperty("formatter");
assert.exists(formatter, "formatter should exist as json property");
assert.equal(formatter.fmtFlags.angMode, 1, "fmtFlags");
assert.equal(formatter.mastUnit.label, "m", "mastUnit is meters");
roundtripThroughJson(model2);
let model = imodel1.models.getModel(IModel.repositoryModelId);
assert.exists(model);
roundtripThroughJson(model);
const code1 = new Code({ spec: "0x1d", scope: "0x1d", value: "A" });
model = imodel1.models.getSubModel(code1);
// By this point, we expect the submodel's class to be in the class registry *cache*
const geomModel = ClassRegistry.getClass(PhysicalModel.classFullName, imodel1);
assert.exists(model);
assert.isTrue(model instanceof geomModel);
roundtripThroughJson(model);
const modelExtents = model.queryExtents();
assert.isBelow(modelExtents.low.x, modelExtents.high.x);
assert.isBelow(modelExtents.low.y, modelExtents.high.y);
assert.isBelow(modelExtents.low.z, modelExtents.high.z);
});
it("should find a tile tree for a geometric model", async () => {
// Note: this is an empty model.
const tree = await imodel1.tiles.requestTileTreeProps("0x1c");
expect(tree).not.to.be.undefined;
expect(tree.id).to.equal("0x1c");
expect(tree.maxTilesToSkip).to.equal(1);
expect(tree.rootTile).not.to.be.undefined;
// Empty model => identity transform
const tf = Transform.fromJSON(tree.location);
expect(tf.matrix.isIdentity).to.be.true;
expect(tf.origin.x).to.equal(0);
expect(tf.origin.y).to.equal(0);
expect(tf.origin.z).to.equal(0);
expect(tree.rootTile.contentId).to.equal("0/0/0/0/1");
// Empty model => null range
const range = Range3d.fromJSON(tree.rootTile.range);
expect(range.isNull).to.be.true;
expect(tree.rootTile.maximumSize).to.equal(0.0); // empty model => undisplayable root tile => size = 0.0
expect(tree.rootTile.isLeaf).to.be.true; // empty model => empty tile
expect(tree.rootTile.contentRange).to.be.undefined;
});
it("should throw on invalid tile requests", async () => {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const _r = __addDisposableResource(env_1, new DisableNativeAssertions(), false);
let error = await getIModelError(imodel1.tiles.requestTileTreeProps("0x12345"));
expectIModelError(IModelStatus.InvalidId, error);
error = await getIModelError(imodel1.tiles.requestTileTreeProps("NotAValidId"));
expectIModelError(IModelStatus.InvalidId, error);
error = await getIModelError(imodel1.tiles.requestTileContent("0x1c", "0/0/0/0"));
expectIModelError(IModelStatus.InvalidId, error);
error = await getIModelError(imodel1.tiles.requestTileContent("0x12345", "0/0/0/0/1"));
expectIModelError(IModelStatus.InvalidId, error);
error = await getIModelError(imodel1.tiles.requestTileContent("0x1c", "V/W/X/Y/Z"));
expectIModelError(IModelStatus.InvalidId, error);
error = await getIModelError(imodel1.tiles.requestTileContent("0x1c", "NotAValidId"));
expectIModelError(IModelStatus.InvalidId, error);
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
__disposeResources(env_1);
}
});
// NOTE: this test can be removed when the deprecated executeQuery method is removed
it("should produce an array of rows", () => {
const rows = IModelTestUtils.executeQuery(imodel1, `SELECT * FROM ${Category.classFullName}`);
assert.exists(rows);
assert.isArray(rows);
assert.isAtLeast(rows.length, 1);
assert.exists(rows[0].id);
assert.notEqual(rows[0].id.value, "");
});
it("should be some categories", () => {
const categorySql = `SELECT ECInstanceId FROM ${Category.classFullName}`;
// eslint-disable-next-line @typescript-eslint/no-deprecated
imodel1.withPreparedStatement(categorySql, (categoryStatement) => {
let numCategories = 0;
while (DbResult.BE_SQLITE_ROW === categoryStatement.step()) {
numCategories++;
const categoryId = categoryStatement.getValue(0).getId();
const category = imodel1.elements.getElement(categoryId);
assert.isTrue(category instanceof Category, "Should be instance of Category");
// verify the default subcategory.
const defaultSubCategoryId = category.myDefaultSubCategoryId();
const defaultSubCategory = imodel1.elements.getElement(defaultSubCategoryId);
assert.isTrue(defaultSubCategory instanceof SubCategory, "defaultSubCategory should be instance of SubCategory");
if (defaultSubCategory instanceof SubCategory) {
assert.isTrue(defaultSubCategory.parent.id === categoryId, "defaultSubCategory id should be prescribed value");
assert.isTrue(defaultSubCategory.getSubCategoryName() === category.code.value, "DefaultSubcategory name should match that of Category");
assert.isTrue(defaultSubCategory.isDefaultSubCategory, "isDefaultSubCategory should return true");
}
// get the subcategories
const subCategorySql = `SELECT ECInstanceId FROM ${SubCategory.classFullName} WHERE Parent.Id=:parentId`;
// eslint-disable-next-line @typescript-eslint/no-deprecated
imodel1.withPreparedStatement(subCategorySql, (subCategoryStatement) => {
let numSubCategories = 0;
subCategoryStatement.bindId("parentId", categoryId);
while (DbResult.BE_SQLITE_ROW === subCategoryStatement.step()) {
numSubCategories++;
const subCategoryId = subCategoryStatement.getValue(0).getId();
const subCategory = imodel1.elements.getElement(subCategoryId);
assert.isTrue(subCategory instanceof SubCategory);
assert.isTrue(subCategory.parent.id === categoryId);
}
assert.isAtLeast(numSubCategories, 1, "Expected query to find at least one SubCategory");
});
}
assert.isAtLeast(numCategories, 1, "Expected query to find some categories");
});
});
it("should be some 2d elements", () => {
const sql = `SELECT ECInstanceId FROM ${DrawingGraphic.classFullName}`;
// eslint-disable-next-line @typescript-eslint/no-deprecated
imodel2.withPreparedStatement(sql, (statement) => {
let numDrawingGraphics = 0;
let found25 = false;
let found26 = false;
while (DbResult.BE_SQLITE_ROW === statement.step()) {
numDrawingGraphics++;
const drawingGraphicId = statement.getValue(0).getId();
const drawingGraphic = imodel2.elements.getElement({ id: drawingGraphicId, wantGeometry: true });
assert.exists(drawingGraphic);
assert.isTrue(drawingGraphic.className === "DrawingGraphic", "Should be instance of DrawingGraphic");
assert.isTrue(drawingGraphic instanceof DrawingGraphic, "Is instance of DrawingGraphic");
assert.isTrue(drawingGraphic instanceof GeometricElement2d, "Is instance of GeometricElement2d");
if (Id64.getLocalId(drawingGraphic.id) === 0x25) {
found25 = true;
assert.isTrue(drawingGraphic.placement.origin.x === 0.0);
assert.isTrue(drawingGraphic.placement.origin.y === 0.0);
assert.isTrue(drawingGraphic.placement.angle.radians === 0.0);
assert.isTrue(drawingGraphic.placement.bbox.low.x === 0.0);
assert.isTrue(drawingGraphic.placement.bbox.low.y === 0.0);
assert.isTrue(drawingGraphic.placement.bbox.high.x === 1.0);
assert.isTrue(drawingGraphic.placement.bbox.high.y === 1.0);
assert.isDefined(drawingGraphic.geom);
}
else if (Id64.getLocalId(drawingGraphic.id) === 0x26) {
found26 = true;
assert.isTrue(drawingGraphic.placement.origin.x === 1.0);
assert.isTrue(drawingGraphic.placement.origin.y === 1.0);
assert.isTrue(drawingGraphic.placement.angle.radians === 0.0);
assert.isTrue(drawingGraphic.placement.bbox.low.x === 0.0);
assert.isTrue(drawingGraphic.placement.bbox.low.y === 0.0);
assert.isTrue(drawingGraphic.placement.bbox.high.x === 2.0);
assert.isTrue(drawingGraphic.placement.bbox.high.y === 2.0);
assert.isDefined(drawingGraphic.geom);
}
}
assert.isAtLeast(numDrawingGraphics, 1, "Expected query to find some DrawingGraphics");
assert.isTrue(found25, "Expected to find a specific element");
assert.isTrue(found26, "Expected to find a specific element");
});
});
it("should be able to query for ViewDefinitionProps", () => {
const viewDefinitionProps = imodel2.views.queryViewDefinitionProps(); // query for all ViewDefinitions
assert.isAtLeast(viewDefinitionProps.length, 3);
assert.isTrue(viewDefinitionProps[0].classFullName.includes("ViewDefinition"));
assert.isFalse(viewDefinitionProps[1].isPrivate);
const spatialViewDefinitionProps = imodel2.views.queryViewDefinitionProps("BisCore.SpatialViewDefinition"); // limit query to SpatialViewDefinitions
assert.isAtLeast(spatialViewDefinitionProps.length, 3);
assert.exists(spatialViewDefinitionProps[2].modelSelectorId);
});
it("should iterate ViewDefinitions", () => {
// imodel2 contains 3 SpatialViewDefinitions and no other views.
let numViews = 0;
let result = imodel2.views.iterateViews(IModelDb.Views.defaultQueryParams, (_view) => {
++numViews;
return true;
});
expect(result).to.be.true;
expect(numViews).to.equal(3);
// Query specifically for spatial views
numViews = 0;
result = imodel2.views.iterateViews({ from: "BisCore.SpatialViewDefinition" }, (view) => {
if (view.isSpatialView())
++numViews;
return view.isSpatialView();
});
expect(result).to.be.true;
expect(numViews).to.equal(3);
// Query specifically for 2d views
numViews = 0;
result = imodel2.views.iterateViews({ from: "BisCore.ViewDefinition2d" }, (_view) => {
++numViews;
return true;
});
expect(result).to.be.true;
expect(numViews).to.equal(0);
// Terminate iteration on first view
numViews = 0;
result = imodel2.views.iterateViews(IModelDb.Views.defaultQueryParams, (_view) => {
++numViews;
return false;
});
expect(result).to.be.f